• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
start()119         Register* start() const { return m_start; }
end()120         Register* end() const { return m_end; }
size()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 
setNumGlobals(size_t numGlobals)129         void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; }
numGlobals()130         int numGlobals() const { return m_numGlobals; }
maxGlobals()131         size_t maxGlobals() const { return m_maxGlobals; }
132 
lastGlobal()133         Register* lastGlobal() const { return m_start - m_numGlobals; }
134 
135         static size_t committedByteCount();
136         static void initializeThreading();
137 
addressOfEnd()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 {
finalize(Handle<Unknown>,void * context)157             virtual void finalize(Handle<Unknown>, void* context)
158             {
159                 static_cast<RegisterFile*>(context)->setNumGlobals(0);
160             }
161         } m_globalObjectOwner;
162     };
163 
RegisterFile(JSGlobalData & globalData,size_t capacity,size_t maxGlobals)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 
shrink(Register * newEnd)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 
grow(Register * newEnd)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