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