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 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<typename RegID, size_t NUM_REGS, typename SpillHint, SpillHint SpillHintMin, SpillHint SpillHintMax> 69class RegisterBank { 70public: 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. 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. 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 } 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. 167 void lock(RegID reg) 168 { 169 ASSERT(reg < NUM_REGS); 170 ++m_data[reg].lockCount; 171 ASSERT(m_data[reg].lockCount); 172 } 173 void unlock(RegID reg) 174 { 175 ASSERT(reg < NUM_REGS); 176 ASSERT(m_data[reg].lockCount); 177 --m_data[reg].lockCount; 178 } 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). 187 VirtualRegister name(RegID reg) 188 { 189 ASSERT(reg < NUM_REGS); 190 return m_data[reg].name; 191 } 192 193#ifndef NDEBUG 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 207private: 208 // Used by 'allocate', above, to update inforamtion in the map. 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 { 232 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