2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #ifndef RegisterFile_h
30 #define RegisterFile_h
33 #include "ExecutableAllocator.h"
37 #include <wtf/Noncopyable.h>
38 #include <wtf/PageReservation.h>
39 #include <wtf/VMTags.h>
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.
48 In a program where function "a" calls function "b" (global code -> a -> b),
49 the register file might look like this:
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 |
60 buffer base (frame 0) frame 1 frame 2
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
67 If we added one global variable to the register file depicted above, it
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-> |
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.
86 For client simplicity, the RegisterFile measures size and capacity from
93 WTF_MAKE_NONCOPYABLE(RegisterFile
);
95 enum CallFrameHeaderEntry
{
96 CallFrameHeaderSize
= 6,
102 ReturnPC
= -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
106 enum { ProgramCodeThisRegister
= -CallFrameHeaderSize
- 1 };
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;
114 RegisterFile(JSGlobalData
&, size_t capacity
= defaultCapacity
, size_t maxGlobals
= defaultMaxGlobals
);
117 void gatherConservativeRoots(ConservativeRoots
&);
119 Register
* start() const { return m_start
; }
120 Register
* end() const { return m_end
; }
121 size_t size() const { return m_end
- m_start
; }
123 void setGlobalObject(JSGlobalObject
*);
124 JSGlobalObject
* globalObject();
126 bool grow(Register
* newEnd
);
127 void shrink(Register
* newEnd
);
129 void setNumGlobals(size_t numGlobals
) { m_numGlobals
= numGlobals
; }
130 int numGlobals() const { return m_numGlobals
; }
131 size_t maxGlobals() const { return m_maxGlobals
; }
133 Register
* lastGlobal() const { return m_start
- m_numGlobals
; }
135 static size_t committedByteCount();
136 static void initializeThreading();
138 Register
* const * addressOfEnd() const
144 void releaseExcessCapacity();
145 void addToCommittedByteCount(long);
147 const size_t m_maxGlobals
;
152 Register
* m_commitEnd
;
153 PageReservation m_reservation
;
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
)
159 static_cast<RegisterFile
*>(context
)->setNumGlobals(0);
161 } m_globalObjectOwner
;
164 inline RegisterFile::RegisterFile(JSGlobalData
& globalData
, size_t capacity
, size_t maxGlobals
)
166 , m_maxGlobals(maxGlobals
)
170 , m_globalObject(globalData
, 0, &m_globalObjectOwner
, this)
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
;
184 m_max
= m_start
+ capacity
;
187 inline void RegisterFile::shrink(Register
* newEnd
)
192 if (m_end
== m_start
&& (m_maxUsed
- m_start
) > maxExcessCapacity
)
193 releaseExcessCapacity();
196 inline bool RegisterFile::grow(Register
* newEnd
)
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
);
211 if (newEnd
> m_maxUsed
)
220 #endif // RegisterFile_h