9c464e314f5aac14af83e59c8d7b5e0544f6a050
[WebKit.git] / Source / JavaScriptCore / interpreter / CLoopStack.cpp
1 /*
2  * Copyright (C) 2008-2017 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 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 #include "config.h"
30 #include "CLoopStack.h"
31
32 #if !ENABLE(JIT)
33
34 #include "CLoopStackInlines.h"
35 #include "ConservativeRoots.h"
36 #include "Interpreter.h"
37 #include "JSCInlines.h"
38 #include "Options.h"
39 #include <wtf/Lock.h>
40
41 namespace JSC {
42
43 static size_t committedBytesCount = 0;
44
45 static size_t commitSize()
46 {
47     static size_t size = std::max<size_t>(16 * 1024, pageSize());
48     return size;
49 }
50
51 static StaticLock stackStatisticsMutex;
52
53 CLoopStack::CLoopStack(VM& vm)
54     : m_vm(vm)
55     , m_topCallFrame(vm.topCallFrame)
56     , m_softReservedZoneSizeInRegisters(0)
57 {
58     size_t capacity = Options::maxPerThreadStackUsage();
59     ASSERT(capacity && isPageAligned(capacity));
60
61     m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize(), capacity), OSAllocator::JSVMStackPages);
62
63     auto* bottomOfStack = highAddress();
64     setCLoopStackLimit(bottomOfStack);
65     ASSERT(m_end == bottomOfStack);
66     m_commitTop = bottomOfStack;
67     m_lastStackPointer = bottomOfStack;
68     m_currentStackPointer = bottomOfStack;
69
70     m_topCallFrame = 0;
71 }
72
73 CLoopStack::~CLoopStack()
74 {
75     ptrdiff_t sizeToDecommit = reinterpret_cast<char*>(highAddress()) - reinterpret_cast<char*>(m_commitTop);
76     m_reservation.decommit(reinterpret_cast<void*>(m_commitTop), sizeToDecommit);
77     addToCommittedByteCount(-sizeToDecommit);
78     m_reservation.deallocate();
79 }
80
81 bool CLoopStack::grow(Register* newTopOfStack)
82 {
83     Register* newTopOfStackWithReservedZone = newTopOfStack - m_softReservedZoneSizeInRegisters;
84
85     // If we have already committed enough memory to satisfy this request,
86     // just update the end pointer and return.
87     if (newTopOfStackWithReservedZone >= m_commitTop) {
88         setCLoopStackLimit(newTopOfStack);
89         return true;
90     }
91
92     // Compute the chunk size of additional memory to commit, and see if we
93     // have it still within our budget. If not, we'll fail to grow and
94     // return false.
95     ptrdiff_t delta = reinterpret_cast<char*>(m_commitTop) - reinterpret_cast<char*>(newTopOfStackWithReservedZone);
96     delta = WTF::roundUpToMultipleOf(commitSize(), delta);
97     Register* newCommitTop = m_commitTop - (delta / sizeof(Register));
98     if (newCommitTop < reservationTop())
99         return false;
100
101     // Otherwise, the growth is still within our budget. Commit it and return true.
102     m_reservation.commit(newCommitTop, delta);
103     addToCommittedByteCount(delta);
104     m_commitTop = newCommitTop;
105     setCLoopStackLimit(newTopOfStack);
106     return true;
107 }
108
109 void CLoopStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks)
110 {
111     conservativeRoots.add(currentStackPointer(), highAddress(), jitStubRoutines, codeBlocks);
112 }
113
114 void CLoopStack::sanitizeStack()
115 {
116 #if !ASAN_ENABLED
117     void* stackTop = currentStackPointer();
118     ASSERT(stackTop <= highAddress());
119     if (m_lastStackPointer < stackTop) {
120         char* begin = reinterpret_cast<char*>(m_lastStackPointer);
121         char* end = reinterpret_cast<char*>(stackTop);
122         memset(begin, 0, end - begin);
123     }
124     
125     m_lastStackPointer = stackTop;
126 #endif
127 }
128
129 void CLoopStack::releaseExcessCapacity()
130 {
131     Register* highAddressWithReservedZone = highAddress() - m_softReservedZoneSizeInRegisters;
132     ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop);
133     m_reservation.decommit(m_commitTop, delta);
134     addToCommittedByteCount(-delta);
135     m_commitTop = highAddressWithReservedZone;
136 }
137
138 void CLoopStack::addToCommittedByteCount(long byteCount)
139 {
140     LockHolder locker(stackStatisticsMutex);
141     ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1);
142     committedBytesCount += byteCount;
143 }
144
145 void CLoopStack::setSoftReservedZoneSize(size_t reservedZoneSize)
146 {
147     m_softReservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register);
148     if (m_commitTop > m_end - m_softReservedZoneSizeInRegisters)
149         grow(m_end);
150 }
151
152 bool CLoopStack::isSafeToRecurse() const
153 {
154     void* reservationLimit = reinterpret_cast<int8_t*>(reservationTop() + m_softReservedZoneSizeInRegisters);
155     return !m_topCallFrame || (m_topCallFrame->topOfFrame() > reservationLimit);
156 }
157
158 size_t CLoopStack::committedByteCount()
159 {
160     LockHolder locker(stackStatisticsMutex);
161     return committedBytesCount;
162 }
163
164 } // namespace JSC
165
166 #endif // !ENABLE(JIT)