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