• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/DFGJITCompiler.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<typename RegID, size_t NUM_REGS, typename SpillHint, SpillHint SpillHintMin, SpillHint SpillHintMax>
69 class RegisterBank {
70 public:
RegisterBank()71     RegisterBank()
72         : m_lastAllocated(NUM_REGS - 1)
73     {
74     }
75 
76     // Allocate a register - this function finds an unlocked register,
77     // locks it, and returns it. If any named registers exist, one
78     // of these should be selected to be allocated. If all unlocked
79     // registers are named, then one of the named registers will need
80     // to be spilled. In this case the register selected to be spilled
81     // will be one of the registers that has the lowest 'spillOrder'
82     // cost associated with it.
83     //
84     // This method select the register to be allocated, and calls the
85     // private 'allocateInternal' method to update internal data
86     // structures accordingly.
allocate(VirtualRegister & spillMe)87     RegID allocate(VirtualRegister &spillMe)
88     {
89         uint32_t currentLowest = NUM_REGS;
90         SpillHint currentSpillOrder = SpillHintMax;
91 
92         // Scan through all register, starting at the last allocated & looping around.
93         ASSERT(m_lastAllocated < NUM_REGS);
94 
95         // This loop is broken into two halves, looping from the last allocated
96         // register (the register returned last time this method was called) to
97         // the maximum register value, then from 0 to the last allocated.
98         // This implements a simple round-robin like approach to try to reduce
99         // thrash, and minimize time spent scanning locked registers in allocation.
100         // If a unlocked and unnamed register is found return it immediately.
101         // Otherwise, find the first unlocked register with the lowest spillOrder.
102         for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
103             // (1) If the current register is locked, it is not a candidate.
104             if (m_data[i].lockCount)
105                 continue;
106             // (2) If the current register's spill order is 0, pick this! – unassigned registers have spill order 0.
107             SpillHint spillOrder = m_data[i].spillOrder;
108             if (!spillOrder)
109                 return allocateInternal(i, spillMe);
110             // If this register is better (has a lower spill order value) than any prior
111             // candidate, then record it.
112             if (spillOrder < currentSpillOrder) {
113                 currentSpillOrder = spillOrder;
114                 currentLowest = i;
115             }
116         }
117         // Loop over the remaining entries.
118         for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
119             if (m_data[i].lockCount)
120                 continue;
121             SpillHint spillOrder = m_data[i].spillOrder;
122             if (!spillOrder)
123                 return allocateInternal(i, spillMe);
124             if (spillOrder < currentSpillOrder) {
125                 currentSpillOrder = spillOrder;
126                 currentLowest = i;
127             }
128         }
129 
130         // Deadlock check - this could only occur is all registers are locked!
131         ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintMax);
132         // There were no available registers; currentLowest will need to be spilled.
133         return allocateInternal(currentLowest, spillMe);
134     }
135 
136     // retain/release - these methods are used to associate/disassociate names
137     // with values in registers. retain should only be called on locked registers.
retain(RegID reg,VirtualRegister name,SpillHint spillOrder)138     void retain(RegID reg, VirtualRegister name, SpillHint spillOrder)
139     {
140         // 'reg' must be a valid, locked register.
141         ASSERT(reg < NUM_REGS);
142         ASSERT(m_data[reg].lockCount);
143         // 'reg' should not currently be named, the new name must be valid.
144         ASSERT(m_data[reg].name == InvalidVirtualRegister);
145         ASSERT(name != InvalidVirtualRegister);
146         // 'reg' should not currently have a spillOrder, the new spill order must be valid.
147         ASSERT(spillOrder && spillOrder < SpillHintMax);
148         ASSERT(m_data[reg].spillOrder == SpillHintMin);
149 
150         m_data[reg].name = name;
151         m_data[reg].spillOrder = spillOrder;
152     }
release(RegID reg)153     void release(RegID reg)
154     {
155         // 'reg' must be a valid register.
156         ASSERT(reg < NUM_REGS);
157         // 'reg' should currently be named.
158         ASSERT(m_data[reg].name != InvalidVirtualRegister);
159         // 'reg' should currently have a valid spill order.
160         ASSERT(m_data[reg].spillOrder > SpillHintMin && m_data[reg].spillOrder < SpillHintMax);
161 
162         m_data[reg].name = InvalidVirtualRegister;
163         m_data[reg].spillOrder = SpillHintMin;
164     }
165 
166     // lock/unlock register, ensures that they are not spilled.
lock(RegID reg)167     void lock(RegID reg)
168     {
169         ASSERT(reg < NUM_REGS);
170         ++m_data[reg].lockCount;
171         ASSERT(m_data[reg].lockCount);
172     }
unlock(RegID reg)173     void unlock(RegID reg)
174     {
175         ASSERT(reg < NUM_REGS);
176         ASSERT(m_data[reg].lockCount);
177         --m_data[reg].lockCount;
178     }
isLocked(RegID reg)179     bool isLocked(RegID reg)
180     {
181         ASSERT(reg < NUM_REGS);
182         return m_data[reg].lockCount;
183     }
184 
185     // Get the name (VirtualRegister) associated with the
186     // given register (or InvalidVirtualRegister for none).
name(RegID reg)187     VirtualRegister name(RegID reg)
188     {
189         ASSERT(reg < NUM_REGS);
190         return m_data[reg].name;
191     }
192 
193 #ifndef NDEBUG
dump()194     void dump()
195     {
196         // For each register, print the VirtualRegister 'name'.
197         for (uint32_t i =0; i < NUM_REGS; ++i) {
198             if (m_data[i].name != InvalidVirtualRegister)
199                 fprintf(stderr, "[%02d]", m_data[i].name);
200             else
201                 fprintf(stderr, "[--]");
202         }
203         fprintf(stderr, "\n");
204     }
205 #endif
206 
207 private:
208     // Used by 'allocate', above, to update inforamtion in the map.
allocateInternal(uint32_t i,VirtualRegister & spillMe)209     RegID allocateInternal(uint32_t i, VirtualRegister &spillMe)
210     {
211         // 'i' must be a valid, unlocked register.
212         ASSERT(i < NUM_REGS && !m_data[i].lockCount);
213 
214         // Return the VirtualRegister of the named value currently stored in
215         // the register being returned - or InvalidVirtualRegister if none.
216         spillMe = m_data[i].name;
217 
218         // Clear any name/spillOrder currently associated with the register,
219         m_data[i] = MapEntry();
220         m_data[i].lockCount = 1;
221         // Mark the register as locked (with a lock count of 1).
222         m_lastAllocated = i;
223         return (RegID)i;
224     }
225 
226     // === MapEntry ===
227     //
228     // This structure provides information for an individual machine register
229     // being managed by the RegisterBank. For each register we track a lock
230     // count, name and spillOrder hint.
231     struct MapEntry {
MapEntryMapEntry232         MapEntry()
233             : name(InvalidVirtualRegister)
234             , spillOrder(SpillHintMin)
235             , lockCount(0)
236         {
237         }
238 
239         VirtualRegister name;
240         SpillHint spillOrder;
241         uint32_t lockCount;
242     };
243 
244     // Holds the current status of all registers.
245     MapEntry m_data[NUM_REGS];
246     // Used to to implement a simple round-robin like allocation scheme.
247     uint32_t m_lastAllocated;
248 };
249 
250 } } // namespace JSC::DFG
251 
252 #endif
253 #endif
254