1 // Copyright 2009 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_REGEXP_REGEXP_STACK_H_ 6 #define V8_REGEXP_REGEXP_STACK_H_ 7 8 #include "src/base/logging.h" 9 #include "src/base/macros.h" 10 #include "src/common/globals.h" 11 12 namespace v8 { 13 namespace internal { 14 15 class RegExpStack; 16 17 // Maintains a per-v8thread stack area that can be used by irregexp 18 // implementation for its backtracking stack. 19 // Since there is only one stack area, the Irregexp implementation is not 20 // re-entrant. I.e., no regular expressions may be executed in the same thread 21 // during a preempted Irregexp execution. 22 class RegExpStackScope { 23 public: 24 // Create and delete an instance to control the life-time of a growing stack. 25 26 // Initializes the stack memory area if necessary. 27 explicit RegExpStackScope(Isolate* isolate); 28 ~RegExpStackScope(); // Releases the stack if it has grown. 29 RegExpStackScope(const RegExpStackScope&) = delete; 30 RegExpStackScope& operator=(const RegExpStackScope&) = delete; 31 stack()32 RegExpStack* stack() const { return regexp_stack_; } 33 34 private: 35 RegExpStack* regexp_stack_; 36 }; 37 38 39 class RegExpStack { 40 public: 41 RegExpStack(); 42 ~RegExpStack(); 43 RegExpStack(const RegExpStack&) = delete; 44 RegExpStack& operator=(const RegExpStack&) = delete; 45 46 // Number of allocated locations on the stack below the limit. 47 // No sequence of pushes must be longer that this without doing a stack-limit 48 // check. 49 static constexpr int kStackLimitSlack = 32; 50 51 // Gives the top of the memory used as stack. stack_base()52 Address stack_base() { 53 DCHECK_NE(0, thread_local_.memory_size_); 54 DCHECK_EQ(thread_local_.memory_top_, 55 thread_local_.memory_ + thread_local_.memory_size_); 56 return reinterpret_cast<Address>(thread_local_.memory_top_); 57 } 58 59 // The total size of the memory allocated for the stack. stack_capacity()60 size_t stack_capacity() { return thread_local_.memory_size_; } 61 62 // If the stack pointer gets below the limit, we should react and 63 // either grow the stack or report an out-of-stack exception. 64 // There is only a limited number of locations below the stack limit, 65 // so users of the stack should check the stack limit during any 66 // sequence of pushes longer that this. limit_address_address()67 Address* limit_address_address() { return &(thread_local_.limit_); } 68 69 // Ensures that there is a memory area with at least the specified size. 70 // If passing zero, the default/minimum size buffer is allocated. 71 Address EnsureCapacity(size_t size); 72 is_in_use()73 bool is_in_use() const { return thread_local_.is_in_use_; } set_is_in_use(bool v)74 void set_is_in_use(bool v) { thread_local_.is_in_use_ = v; } 75 76 // Thread local archiving. ArchiveSpacePerThread()77 static constexpr int ArchiveSpacePerThread() { 78 return static_cast<int>(kThreadLocalSize); 79 } 80 char* ArchiveStack(char* to); 81 char* RestoreStack(char* from); FreeThreadResources()82 void FreeThreadResources() { thread_local_.ResetToStaticStack(this); } 83 84 // Maximal size of allocated stack area. 85 static constexpr size_t kMaximumStackSize = 64 * MB; 86 87 private: 88 // Artificial limit used when the thread-local state has been destroyed. 89 static const Address kMemoryTop = 90 static_cast<Address>(static_cast<uintptr_t>(-1)); 91 92 // Minimal size of dynamically-allocated stack area. 93 static constexpr size_t kMinimumDynamicStackSize = 1 * KB; 94 95 // In addition to dynamically-allocated, variable-sized stacks, we also have 96 // a statically allocated and sized area that is used whenever no dynamic 97 // stack is allocated. This guarantees that a stack is always available and 98 // we can skip availability-checks later on. 99 // It's double the slack size to ensure that we have a bit of breathing room 100 // before NativeRegExpMacroAssembler::GrowStack must be called. 101 static constexpr size_t kStaticStackSize = 102 2 * kStackLimitSlack * kSystemPointerSize; 103 byte static_stack_[kStaticStackSize] = {0}; 104 105 STATIC_ASSERT(kStaticStackSize <= kMaximumStackSize); 106 107 // Structure holding the allocated memory, size and limit. 108 struct ThreadLocal { ThreadLocalThreadLocal109 explicit ThreadLocal(RegExpStack* regexp_stack) { 110 ResetToStaticStack(regexp_stack); 111 } 112 113 // If memory_size_ > 0 then memory_ and memory_top_ must be non-nullptr 114 // and memory_top_ = memory_ + memory_size_ 115 byte* memory_ = nullptr; 116 byte* memory_top_ = nullptr; 117 size_t memory_size_ = 0; 118 Address limit_ = kNullAddress; 119 bool owns_memory_ = false; // Whether memory_ is owned and must be freed. 120 bool is_in_use_ = false; // To guard against reentrancy. 121 122 void ResetToStaticStack(RegExpStack* regexp_stack); 123 void FreeAndInvalidate(); 124 }; 125 static constexpr size_t kThreadLocalSize = sizeof(ThreadLocal); 126 127 // Address of top of memory used as stack. memory_top_address_address()128 Address memory_top_address_address() { 129 return reinterpret_cast<Address>(&thread_local_.memory_top_); 130 } 131 132 // Resets the buffer if it has grown beyond the default/minimum size. 133 // After this, the buffer is either the default size, or it is empty, so 134 // you have to call EnsureCapacity before using it again. 135 void Reset(); 136 137 ThreadLocal thread_local_; 138 Isolate* isolate_; 139 140 friend class ExternalReference; 141 friend class Isolate; 142 friend class RegExpStackScope; 143 }; 144 145 } // namespace internal 146 } // namespace v8 147 148 #endif // V8_REGEXP_REGEXP_STACK_H_ 149