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
41namespace 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