Home | History | Annotate | Download | only in interpreter
      1 /*
      2  * Copyright (C) 2008, 2009 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #ifndef RegisterFile_h
     30 #define RegisterFile_h
     31 
     32 #include "Heap.h"
     33 #include "ExecutableAllocator.h"
     34 #include "Register.h"
     35 #include "Weak.h"
     36 #include <stdio.h>
     37 #include <wtf/Noncopyable.h>
     38 #include <wtf/PageReservation.h>
     39 #include <wtf/VMTags.h>
     40 
     41 namespace JSC {
     42 
     43 /*
     44     A register file is a stack of register frames. We represent a register
     45     frame by its offset from "base", the logical first entry in the register
     46     file. The bottom-most register frame's offset from base is 0.
     47 
     48     In a program where function "a" calls function "b" (global code -> a -> b),
     49     the register file might look like this:
     50 
     51     |       global frame     |        call frame      |        call frame      |     spare capacity     |
     52     -----------------------------------------------------------------------------------------------------
     53     |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 | 13 | 14 |    |    |    |    |    | <-- index in buffer
     54     -----------------------------------------------------------------------------------------------------
     55     | -3 | -2 | -1 |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 |    |    |    |    |    | <-- index relative to base
     56     -----------------------------------------------------------------------------------------------------
     57     |    <-globals | temps-> |  <-vars | temps->      |                 <-vars |
     58        ^              ^                   ^                                       ^
     59        |              |                   |                                       |
     60      buffer    base (frame 0)          frame 1                                 frame 2
     61 
     62     Since all variables, including globals, are accessed by negative offsets
     63     from their register frame pointers, to keep old global offsets correct, new
     64     globals must appear at the beginning of the register file, shifting base
     65     to the right.
     66 
     67     If we added one global variable to the register file depicted above, it
     68     would look like this:
     69 
     70     |         global frame        |<                                                                    >
     71     ------------------------------->                                                                    <
     72     |  0 |  1 |  2 |  3 |  4 |  5 |<                             >snip<                                 > <-- index in buffer
     73     ------------------------------->                                                                    <
     74     | -4 | -3 | -2 | -1 |  0 |  1 |<                                                                    > <-- index relative to base
     75     ------------------------------->                                                                    <
     76     |         <-globals | temps-> |
     77        ^                   ^
     78        |                   |
     79      buffer         base (frame 0)
     80 
     81     As you can see, global offsets relative to base have stayed constant,
     82     but base itself has moved. To keep up with possible changes to base,
     83     clients keep an indirect pointer, so their calculations update
     84     automatically when base changes.
     85 
     86     For client simplicity, the RegisterFile measures size and capacity from
     87     "base", not "buffer".
     88 */
     89 
     90     class JSGlobalObject;
     91 
     92     class RegisterFile {
     93         WTF_MAKE_NONCOPYABLE(RegisterFile);
     94     public:
     95         enum CallFrameHeaderEntry {
     96             CallFrameHeaderSize = 6,
     97 
     98             ArgumentCount = -6,
     99             CallerFrame = -5,
    100             Callee = -4,
    101             ScopeChain = -3,
    102             ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
    103             CodeBlock = -1,
    104         };
    105 
    106         enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 };
    107 
    108         static const size_t defaultCapacity = 512 * 1024;
    109         static const size_t defaultMaxGlobals = 8 * 1024;
    110         static const size_t commitSize = 16 * 1024;
    111         // Allow 8k of excess registers before we start trying to reap the registerfile
    112         static const ptrdiff_t maxExcessCapacity = 8 * 1024;
    113 
    114         RegisterFile(JSGlobalData&, size_t capacity = defaultCapacity, size_t maxGlobals = defaultMaxGlobals);
    115         ~RegisterFile();
    116 
    117         void gatherConservativeRoots(ConservativeRoots&);
    118 
    119         Register* start() const { return m_start; }
    120         Register* end() const { return m_end; }
    121         size_t size() const { return m_end - m_start; }
    122 
    123         void setGlobalObject(JSGlobalObject*);
    124         JSGlobalObject* globalObject();
    125 
    126         bool grow(Register* newEnd);
    127         void shrink(Register* newEnd);
    128 
    129         void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; }
    130         int numGlobals() const { return m_numGlobals; }
    131         size_t maxGlobals() const { return m_maxGlobals; }
    132 
    133         Register* lastGlobal() const { return m_start - m_numGlobals; }
    134 
    135         static size_t committedByteCount();
    136         static void initializeThreading();
    137 
    138         Register* const * addressOfEnd() const
    139         {
    140             return &m_end;
    141         }
    142 
    143     private:
    144         void releaseExcessCapacity();
    145         void addToCommittedByteCount(long);
    146         size_t m_numGlobals;
    147         const size_t m_maxGlobals;
    148         Register* m_start;
    149         Register* m_end;
    150         Register* m_max;
    151         Register* m_maxUsed;
    152         Register* m_commitEnd;
    153         PageReservation m_reservation;
    154 
    155         Weak<JSGlobalObject> m_globalObject; // The global object whose vars are currently stored in the register file.
    156         class GlobalObjectOwner : public WeakHandleOwner {
    157             virtual void finalize(Handle<Unknown>, void* context)
    158             {
    159                 static_cast<RegisterFile*>(context)->setNumGlobals(0);
    160             }
    161         } m_globalObjectOwner;
    162     };
    163 
    164     inline RegisterFile::RegisterFile(JSGlobalData& globalData, size_t capacity, size_t maxGlobals)
    165         : m_numGlobals(0)
    166         , m_maxGlobals(maxGlobals)
    167         , m_start(0)
    168         , m_end(0)
    169         , m_max(0)
    170         , m_globalObject(globalData, 0, &m_globalObjectOwner, this)
    171     {
    172         ASSERT(maxGlobals && isPageAligned(maxGlobals));
    173         ASSERT(capacity && isPageAligned(capacity));
    174         size_t bufferLength = (capacity + maxGlobals) * sizeof(Register);
    175         m_reservation = PageReservation::reserve(roundUpAllocationSize(bufferLength, commitSize), OSAllocator::JSVMStackPages);
    176         void* base = m_reservation.base();
    177         size_t committedSize = roundUpAllocationSize(maxGlobals * sizeof(Register), commitSize);
    178         m_reservation.commit(base, committedSize);
    179         addToCommittedByteCount(static_cast<long>(committedSize));
    180         m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(base) + committedSize);
    181         m_start = static_cast<Register*>(base) + maxGlobals;
    182         m_end = m_start;
    183         m_maxUsed = m_end;
    184         m_max = m_start + capacity;
    185     }
    186 
    187     inline void RegisterFile::shrink(Register* newEnd)
    188     {
    189         if (newEnd >= m_end)
    190             return;
    191         m_end = newEnd;
    192         if (m_end == m_start && (m_maxUsed - m_start) > maxExcessCapacity)
    193             releaseExcessCapacity();
    194     }
    195 
    196     inline bool RegisterFile::grow(Register* newEnd)
    197     {
    198         if (newEnd < m_end)
    199             return true;
    200 
    201         if (newEnd > m_max)
    202             return false;
    203 
    204         if (newEnd > m_commitEnd) {
    205             size_t size = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize);
    206             m_reservation.commit(m_commitEnd, size);
    207             addToCommittedByteCount(static_cast<long>(size));
    208             m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + size);
    209         }
    210 
    211         if (newEnd > m_maxUsed)
    212             m_maxUsed = newEnd;
    213 
    214         m_end = newEnd;
    215         return true;
    216     }
    217 
    218 } // namespace JSC
    219 
    220 #endif // RegisterFile_h
    221