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 "Collector.h"
     33 #include "ExecutableAllocator.h"
     34 #include "Register.h"
     35 #include <stdio.h>
     36 #include <wtf/Noncopyable.h>
     37 #include <wtf/VMTags.h>
     38 
     39 #if HAVE(MMAP)
     40 #include <errno.h>
     41 #include <sys/mman.h>
     42 #endif
     43 
     44 namespace JSC {
     45 
     46 /*
     47     A register file is a stack of register frames. We represent a register
     48     frame by its offset from "base", the logical first entry in the register
     49     file. The bottom-most register frame's offset from base is 0.
     50 
     51     In a program where function "a" calls function "b" (global code -> a -> b),
     52     the register file might look like this:
     53 
     54     |       global frame     |        call frame      |        call frame      |     spare capacity     |
     55     -----------------------------------------------------------------------------------------------------
     56     |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 | 13 | 14 |    |    |    |    |    | <-- index in buffer
     57     -----------------------------------------------------------------------------------------------------
     58     | -3 | -2 | -1 |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 |    |    |    |    |    | <-- index relative to base
     59     -----------------------------------------------------------------------------------------------------
     60     |    <-globals | temps-> |  <-vars | temps->      |                 <-vars |
     61        ^              ^                   ^                                       ^
     62        |              |                   |                                       |
     63      buffer    base (frame 0)          frame 1                                 frame 2
     64 
     65     Since all variables, including globals, are accessed by negative offsets
     66     from their register frame pointers, to keep old global offsets correct, new
     67     globals must appear at the beginning of the register file, shifting base
     68     to the right.
     69 
     70     If we added one global variable to the register file depicted above, it
     71     would look like this:
     72 
     73     |         global frame        |<                                                                    >
     74     ------------------------------->                                                                    <
     75     |  0 |  1 |  2 |  3 |  4 |  5 |<                             >snip<                                 > <-- index in buffer
     76     ------------------------------->                                                                    <
     77     | -4 | -3 | -2 | -1 |  0 |  1 |<                                                                    > <-- index relative to base
     78     ------------------------------->                                                                    <
     79     |         <-globals | temps-> |
     80        ^                   ^
     81        |                   |
     82      buffer         base (frame 0)
     83 
     84     As you can see, global offsets relative to base have stayed constant,
     85     but base itself has moved. To keep up with possible changes to base,
     86     clients keep an indirect pointer, so their calculations update
     87     automatically when base changes.
     88 
     89     For client simplicity, the RegisterFile measures size and capacity from
     90     "base", not "buffer".
     91 */
     92 
     93     class JSGlobalObject;
     94 
     95     class RegisterFile : public Noncopyable {
     96         friend class JIT;
     97     public:
     98         enum CallFrameHeaderEntry {
     99             CallFrameHeaderSize = 8,
    100 
    101             CodeBlock = -8,
    102             ScopeChain = -7,
    103             CallerFrame = -6,
    104             ReturnPC = -5, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
    105             ReturnValueRegister = -4,
    106             ArgumentCount = -3,
    107             Callee = -2,
    108             OptionalCalleeArguments = -1,
    109         };
    110 
    111         enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 };
    112         enum { ArgumentsRegister = 0 };
    113 
    114         static const size_t defaultCapacity = 524288;
    115         static const size_t defaultMaxGlobals = 8192;
    116         static const size_t commitSize = 1 << 14;
    117         // Allow 8k of excess registers before we start trying to reap the registerfile
    118         static const ptrdiff_t maxExcessCapacity = 8 * 1024;
    119 
    120         RegisterFile(size_t capacity = defaultCapacity, size_t maxGlobals = defaultMaxGlobals);
    121         ~RegisterFile();
    122 
    123         Register* start() const { return m_start; }
    124         Register* end() const { return m_end; }
    125         size_t size() const { return m_end - m_start; }
    126 
    127         void setGlobalObject(JSGlobalObject* globalObject) { m_globalObject = globalObject; }
    128         JSGlobalObject* globalObject() { return m_globalObject; }
    129 
    130         bool grow(Register* newEnd);
    131         void shrink(Register* newEnd);
    132 
    133         void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; }
    134         int numGlobals() const { return m_numGlobals; }
    135         size_t maxGlobals() const { return m_maxGlobals; }
    136 
    137         Register* lastGlobal() const { return m_start - m_numGlobals; }
    138 
    139         void markGlobals(MarkStack& markStack, Heap* heap) { heap->markConservatively(markStack, lastGlobal(), m_start); }
    140         void markCallFrames(MarkStack& markStack, Heap* heap) { heap->markConservatively(markStack, m_start, m_end); }
    141 
    142     private:
    143         void releaseExcessCapacity();
    144         size_t m_numGlobals;
    145         const size_t m_maxGlobals;
    146         Register* m_start;
    147         Register* m_end;
    148         Register* m_max;
    149         Register* m_buffer;
    150         Register* m_maxUsed;
    151 
    152 #if HAVE(VIRTUALALLOC)
    153         Register* m_commitEnd;
    154 #endif
    155 
    156         JSGlobalObject* m_globalObject; // The global object whose vars are currently stored in the register file.
    157     };
    158 
    159     // FIXME: Add a generic getpagesize() to WTF, then move this function to WTF as well.
    160     inline bool isPageAligned(size_t size) { return size != 0 && size % (8 * 1024) == 0; }
    161 
    162     inline RegisterFile::RegisterFile(size_t capacity, size_t maxGlobals)
    163         : m_numGlobals(0)
    164         , m_maxGlobals(maxGlobals)
    165         , m_start(0)
    166         , m_end(0)
    167         , m_max(0)
    168         , m_buffer(0)
    169         , m_globalObject(0)
    170     {
    171         // Verify that our values will play nice with mmap and VirtualAlloc.
    172         ASSERT(isPageAligned(maxGlobals));
    173         ASSERT(isPageAligned(capacity));
    174 
    175         size_t bufferLength = (capacity + maxGlobals) * sizeof(Register);
    176     #if HAVE(MMAP)
    177         m_buffer = static_cast<Register*>(mmap(0, bufferLength, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, VM_TAG_FOR_REGISTERFILE_MEMORY, 0));
    178         if (m_buffer == MAP_FAILED) {
    179 #if OS(WINCE)
    180             fprintf(stderr, "Could not allocate register file: %d\n", GetLastError());
    181 #else
    182             fprintf(stderr, "Could not allocate register file: %d\n", errno);
    183 #endif
    184             CRASH();
    185         }
    186     #elif HAVE(VIRTUALALLOC)
    187         m_buffer = static_cast<Register*>(VirtualAlloc(0, roundUpAllocationSize(bufferLength, commitSize), MEM_RESERVE, PAGE_READWRITE));
    188         if (!m_buffer) {
    189 #if OS(WINCE)
    190             fprintf(stderr, "Could not allocate register file: %d\n", GetLastError());
    191 #else
    192             fprintf(stderr, "Could not allocate register file: %d\n", errno);
    193 #endif
    194             CRASH();
    195         }
    196         size_t committedSize = roundUpAllocationSize(maxGlobals * sizeof(Register), commitSize);
    197         void* commitCheck = VirtualAlloc(m_buffer, committedSize, MEM_COMMIT, PAGE_READWRITE);
    198         if (commitCheck != m_buffer) {
    199 #if OS(WINCE)
    200             fprintf(stderr, "Could not allocate register file: %d\n", GetLastError());
    201 #else
    202             fprintf(stderr, "Could not allocate register file: %d\n", errno);
    203 #endif
    204             CRASH();
    205         }
    206         m_commitEnd = reinterpret_cast<Register*>(reinterpret_cast<char*>(m_buffer) + committedSize);
    207     #else
    208         /*
    209          * If neither MMAP nor VIRTUALALLOC are available - use fastMalloc instead.
    210          *
    211          * Please note that this is the fallback case, which is non-optimal.
    212          * If any possible, the platform should provide for a better memory
    213          * allocation mechanism that allows for "lazy commit" or dynamic
    214          * pre-allocation, similar to mmap or VirtualAlloc, to avoid waste of memory.
    215          */
    216         m_buffer = static_cast<Register*>(fastMalloc(bufferLength));
    217     #endif
    218         m_start = m_buffer + maxGlobals;
    219         m_end = m_start;
    220         m_maxUsed = m_end;
    221         m_max = m_start + capacity;
    222     }
    223 
    224     inline void RegisterFile::shrink(Register* newEnd)
    225     {
    226         if (newEnd >= m_end)
    227             return;
    228         m_end = newEnd;
    229         if (m_end == m_start && (m_maxUsed - m_start) > maxExcessCapacity)
    230             releaseExcessCapacity();
    231     }
    232 
    233     inline bool RegisterFile::grow(Register* newEnd)
    234     {
    235         if (newEnd < m_end)
    236             return true;
    237 
    238         if (newEnd > m_max)
    239             return false;
    240 
    241 #if !HAVE(MMAP) && HAVE(VIRTUALALLOC)
    242         if (newEnd > m_commitEnd) {
    243             size_t size = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize);
    244             if (!VirtualAlloc(m_commitEnd, size, MEM_COMMIT, PAGE_READWRITE)) {
    245 #if OS(WINCE)
    246                 fprintf(stderr, "Could not allocate register file: %d\n", GetLastError());
    247 #else
    248                 fprintf(stderr, "Could not allocate register file: %d\n", errno);
    249 #endif
    250                 CRASH();
    251             }
    252             m_commitEnd = reinterpret_cast<Register*>(reinterpret_cast<char*>(m_commitEnd) + size);
    253         }
    254 #endif
    255 
    256         if (newEnd > m_maxUsed)
    257             m_maxUsed = newEnd;
    258 
    259         m_end = newEnd;
    260         return true;
    261     }
    262 
    263 } // namespace JSC
    264 
    265 #endif // RegisterFile_h
    266