/ interpreter / CLoopStack.cpp
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(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  
 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 Lock 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      capacity = WTF::roundUpToMultipleOf(pageSize(), capacity);
 60      ASSERT(capacity && isPageAligned(capacity));
 61  
 62      m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize(), capacity), OSAllocator::UnknownUsage);
 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  
 74  CLoopStack::~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  
 82  bool 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  
111  void CLoopStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks)
112  {
113      conservativeRoots.add(currentStackPointer(), highAddress(), jitStubRoutines, codeBlocks);
114  }
115  
116  void 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  
131  void 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  
140  void CLoopStack::addToCommittedByteCount(long byteCount)
141  {
142      LockHolder locker(stackStatisticsMutex);
143      ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1);
144      committedBytesCount += byteCount;
145  }
146  
147  void 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  
154  bool CLoopStack::isSafeToRecurse() const
155  {
156      void* reservationLimit = reinterpret_cast<int8_t*>(reservationTop() + m_softReservedZoneSizeInRegisters);
157      return !m_topCallFrame || (m_topCallFrame->topOfFrame() > reservationLimit);
158  }
159  
160  size_t CLoopStack::committedByteCount()
161  {
162      LockHolder locker(stackStatisticsMutex);
163      return committedBytesCount;
164  }
165  
166  } // namespace JSC
167  
168  #endif // ENABLE(C_LOOP)