Home | History | Annotate | Download | only in dfg
      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:
     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 
    207 private:
    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