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