ZoneShim.h (13959B)
1 // Copyright 2019 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_UTIL_ZONE_H_ 6 #define V8_UTIL_ZONE_H_ 7 8 #include <list> 9 #include <map> 10 #include <set> 11 #include <unordered_map> 12 #include <vector> 13 14 #include "ds/LifoAlloc.h" 15 #include "ds/Sort.h" 16 #include "irregexp/util/VectorShim.h" 17 18 namespace v8 { 19 namespace internal { 20 21 // V8::Zone ~= LifoAlloc 22 class Zone { 23 public: 24 Zone(js::LifoAlloc& alloc) : lifoAlloc_(alloc) {} 25 26 template <typename T, typename... Args> 27 T* New(Args&&... args) { 28 js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_); 29 js::AutoEnterOOMUnsafeRegion oomUnsafe; 30 void* memory = lifoAlloc_.alloc(sizeof(T)); 31 if (!memory) { 32 oomUnsafe.crash("Irregexp Zone::New"); 33 } 34 return new (memory) T(std::forward<Args>(args)...); 35 } 36 37 // Allocates uninitialized memory for 'length' number of T instances. 38 template <typename T> 39 T* NewArray(size_t length) { 40 js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_); 41 js::AutoEnterOOMUnsafeRegion oomUnsafe; 42 size_t numBytes = length * sizeof(T); 43 if (MOZ_UNLIKELY(numBytes > INT_MAX)) { 44 oomUnsafe.crash("Irregexp Zone::New"); 45 } 46 void* memory = lifoAlloc_.alloc(length * sizeof(T)); 47 if (MOZ_UNLIKELY(!memory)) { 48 oomUnsafe.crash("Irregexp Zone::New"); 49 } 50 return static_cast<T*>(memory); 51 } 52 53 void DeleteAll() { lifoAlloc_.freeAll(); } 54 55 // Returns true if the total memory allocated exceeds a threshold. 56 static const size_t kExcessLimit = 256 * 1024 * 1024; 57 bool excess_allocation() const { 58 return lifoAlloc_.computedSizeOfExcludingThis() > kExcessLimit; 59 } 60 61 js::LifoAlloc& inner() { return lifoAlloc_; } 62 63 private: 64 js::LifoAlloc& lifoAlloc_; 65 }; 66 67 // Superclass for classes allocated in a Zone. 68 // Based on: https://github.com/v8/v8/blob/master/src/zone/zone.h 69 class ZoneObject { 70 public: 71 // new (zone) SomeObject(...) was the old pattern. 72 // Delete the constructor to avoid using it accidentally. 73 void* operator new(size_t size, Zone* zone) = delete; 74 75 // Allow non-allocating placement new 76 void* operator new(size_t size, void* ptr) { return ptr; } 77 78 // Ideally, the delete operator should be private instead of 79 // public, but unfortunately the compiler sometimes synthesizes 80 // (unused) destructors for classes derived from ZoneObject, which 81 // require the operator to be visible. MSVC requires the delete 82 // operator to be public. 83 84 // ZoneObjects should never be deleted individually; use 85 // Zone::DeleteAll() to delete all zone objects in one go. 86 void operator delete(void*, size_t) { MOZ_CRASH("unreachable"); } 87 void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); } 88 }; 89 90 // ZoneLists are growable lists with constant-time access to the 91 // elements. The list itself and all its elements are allocated in the 92 // Zone. ZoneLists cannot be deleted individually; you can delete all 93 // objects in the Zone by calling Zone::DeleteAll(). 94 // Used throughout irregexp. 95 // Based on: https://github.com/v8/v8/blob/master/src/zone/zone-list.h 96 template <typename T> 97 class ZoneList final : public ZoneObject { 98 public: 99 // Construct a new ZoneList with the given capacity; the length is 100 // always zero. The capacity must be non-negative. 101 ZoneList(int capacity, Zone* zone) : capacity_(capacity) { 102 data_ = (capacity_ > 0) ? zone->NewArray<T>(capacity_) : nullptr; 103 } 104 // Construct a new ZoneList by copying the elements of the given ZoneList. 105 ZoneList(const ZoneList<T>& other, Zone* zone) 106 : ZoneList(other.length(), zone) { 107 AddAll(other, zone); 108 } 109 110 // Construct a new ZoneList by copying the elements of the given vector. 111 ZoneList(const base::Vector<const T>& other, Zone* zone) 112 : ZoneList(other.length(), zone) { 113 AddAll(other, zone); 114 } 115 116 ZoneList(ZoneList<T>&& other) { *this = std::move(other); } 117 118 ZoneList& operator=(ZoneList&& other) { 119 MOZ_ASSERT(!data_); 120 data_ = other.data_; 121 capacity_ = other.capacity_; 122 length_ = other.length_; 123 other.Clear(); 124 return *this; 125 } 126 127 // Returns a reference to the element at index i. This reference is not safe 128 // to use after operations that can change the list's backing store 129 // (e.g. Add). 130 inline T& operator[](int i) const { 131 MOZ_ASSERT(i >= 0); 132 MOZ_ASSERT(static_cast<unsigned>(i) < static_cast<unsigned>(length_)); 133 return data_[i]; 134 } 135 inline T& at(int i) const { return operator[](i); } 136 inline T& last() const { return at(length_ - 1); } 137 inline T& first() const { return at(0); } 138 139 using iterator = T*; 140 inline iterator begin() const { return &data_[0]; } 141 inline iterator end() const { return &data_[length_]; } 142 143 inline bool is_empty() const { return length_ == 0; } 144 inline int length() const { return length_; } 145 inline int capacity() const { return capacity_; } 146 147 base::Vector<T> ToVector() const { return base::Vector<T>(data_, length_); } 148 base::Vector<T> ToVector(int start, int length) const { 149 return base::Vector<T>(data_ + start, std::min(length_ - start, length)); 150 } 151 152 base::Vector<const T> ToConstVector() const { 153 return base::Vector<const T>(data_, length_); 154 } 155 156 // Adds a copy of the given 'element' to the end of the list, 157 // expanding the list if necessary. 158 void Add(const T& element, Zone* zone) { 159 if (length_ < capacity_) { 160 data_[length_++] = element; 161 } else { 162 ZoneList<T>::ResizeAdd(element, zone); 163 } 164 } 165 // Add all the elements from the argument list to this list. 166 void AddAll(const ZoneList<T>& other, Zone* zone) { 167 AddAll(other.ToVector(), zone); 168 } 169 // Add all the elements from the vector to this list. 170 void AddAll(const base::Vector<const T>& other, Zone* zone) { 171 int result_length = length_ + other.length(); 172 if (capacity_ < result_length) { 173 Resize(result_length, zone); 174 } 175 if (std::is_fundamental<T>()) { 176 memcpy(data_ + length_, other.begin(), sizeof(*data_) * other.length()); 177 } else { 178 for (int i = 0; i < other.length(); i++) { 179 data_[length_ + i] = other.at(i); 180 } 181 } 182 length_ = result_length; 183 } 184 185 // Overwrites the element at the specific index. 186 void Set(int index, const T& element) { 187 MOZ_ASSERT(index >= 0 && index <= length_); 188 data_[index] = element; 189 } 190 191 // Removes the i'th element without deleting it even if T is a 192 // pointer type; moves all elements above i "down". Returns the 193 // removed element. This function's complexity is linear in the 194 // size of the list. 195 T Remove(int i) { 196 T element = at(i); 197 length_--; 198 while (i < length_) { 199 data_[i] = data_[i + 1]; 200 i++; 201 } 202 return element; 203 } 204 205 // Removes the last element without deleting it even if T is a 206 // pointer type. Returns the removed element. 207 inline T RemoveLast() { return Remove(length_ - 1); } 208 209 // Clears the list, setting the capacity and length to 0. 210 inline void Clear() { 211 data_ = nullptr; 212 capacity_ = 0; 213 length_ = 0; 214 } 215 216 // Drops all but the first 'pos' elements from the list. 217 inline void Rewind(int pos) { 218 MOZ_ASSERT(0 <= pos && pos <= length_); 219 length_ = pos; 220 } 221 222 inline bool Contains(const T& elm) const { 223 for (int i = 0; i < length_; i++) { 224 if (data_[i] == elm) return true; 225 } 226 return false; 227 } 228 229 template <typename CompareFunction> 230 void StableSort(CompareFunction cmp, size_t start, size_t length) { 231 js::AutoEnterOOMUnsafeRegion oomUnsafe; 232 T* scratch = static_cast<T*>(js_malloc(length * sizeof(T))); 233 if (!scratch) { 234 oomUnsafe.crash("Irregexp stable sort scratch space"); 235 } 236 auto comparator = [cmp](const T& a, const T& b, bool* lessOrEqual) { 237 *lessOrEqual = cmp(&a, &b) <= 0; 238 return true; 239 }; 240 MOZ_ALWAYS_TRUE( 241 js::MergeSort(begin() + start, length, scratch, comparator)); 242 js_free(scratch); 243 } 244 245 void operator delete(void* pointer) { MOZ_CRASH("unreachable"); } 246 void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); } 247 248 private: 249 T* data_ = nullptr; 250 int capacity_ = 0; 251 int length_ = 0; 252 253 // Increase the capacity of a full list, and add an element. 254 // List must be full already. 255 void ResizeAdd(const T& element, Zone* zone) { 256 MOZ_ASSERT(length_ >= capacity_); 257 // Grow the list capacity by 100%, but make sure to let it grow 258 // even when the capacity is zero (possible initial case). 259 int new_capacity = 1 + 2 * capacity_; 260 // Since the element reference could be an element of the list, copy 261 // it out of the old backing storage before resizing. 262 T temp = element; 263 Resize(new_capacity, zone); 264 data_[length_++] = temp; 265 } 266 267 // Resize the list. 268 void Resize(int new_capacity, Zone* zone) { 269 MOZ_ASSERT(length_ <= new_capacity); 270 static_assert(std::is_trivially_copyable<T>::value); 271 T* new_data = zone->NewArray<T>(new_capacity); 272 if (length_ > 0) { 273 memcpy(new_data, data_, length_ * sizeof(T)); 274 } 275 data_ = new_data; 276 capacity_ = new_capacity; 277 } 278 279 ZoneList& operator=(const ZoneList&) = delete; 280 ZoneList() = delete; 281 ZoneList(const ZoneList&) = delete; 282 }; 283 284 // Based on: https://github.com/v8/v8/blob/master/src/zone/zone-allocator.h 285 template <typename T> 286 class ZoneAllocator { 287 public: 288 using pointer = T*; 289 using const_pointer = const T*; 290 using reference = T&; 291 using const_reference = const T&; 292 using value_type = T; 293 using size_type = size_t; 294 using difference_type = ptrdiff_t; 295 template <class O> 296 struct rebind { 297 using other = ZoneAllocator<O>; 298 }; 299 300 explicit ZoneAllocator(Zone* zone) : zone_(zone) {} 301 template <typename U> 302 ZoneAllocator(const ZoneAllocator<U>& other) 303 : ZoneAllocator<T>(other.zone_) {} 304 template <typename U> 305 friend class ZoneAllocator; 306 307 T* allocate(size_t n) { return zone_->NewArray<T>(n); } 308 void deallocate(T* p, size_t) {} // noop for zones 309 310 bool operator==(ZoneAllocator const& other) const { 311 return zone_ == other.zone_; 312 } 313 bool operator!=(ZoneAllocator const& other) const { 314 return zone_ != other.zone_; 315 } 316 317 using Policy = js::LifoAllocPolicy<js::Fallible>; 318 Policy policy() const { 319 return js::LifoAllocPolicy<js::Fallible>(zone_->inner()); 320 } 321 322 private: 323 Zone* zone_; 324 }; 325 326 // Zone wrappers for std containers: 327 // Origin: 328 // https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-containers.h#L25-L169 329 330 // A wrapper subclass for std::vector to make it easy to construct one 331 // that uses a zone allocator. 332 // Used throughout irregexp 333 template <typename T> 334 class ZoneVector : public std::vector<T, ZoneAllocator<T>> { 335 public: 336 ZoneVector(Zone* zone) 337 : std::vector<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {} 338 339 // Constructs a new vector and fills it with {size} elements, each 340 // constructed via the default constructor. 341 ZoneVector(size_t size, Zone* zone) 342 : std::vector<T, ZoneAllocator<T>>(size, T(), ZoneAllocator<T>(zone)) {} 343 344 // Constructs a new vector and fills it with the contents of the range 345 // [first, last). 346 template <class Iter> 347 ZoneVector(Iter first, Iter last, Zone* zone) 348 : std::vector<T, ZoneAllocator<T>>(first, last, ZoneAllocator<T>(zone)) {} 349 }; 350 351 // A wrapper subclass for std::list to make it easy to construct one 352 // that uses a zone allocator. 353 // Used in regexp-bytecode-peephole.cc 354 template <typename T> 355 class ZoneLinkedList : public std::list<T, ZoneAllocator<T>> { 356 public: 357 // Constructs an empty list. 358 explicit ZoneLinkedList(Zone* zone) 359 : std::list<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {} 360 }; 361 362 // A wrapper subclass for std::set to make it easy to construct one that uses 363 // a zone allocator. 364 // Used in regexp-parser.cc 365 template <typename K, typename Compare = std::less<K>> 366 class ZoneSet : public std::set<K, Compare, ZoneAllocator<K>> { 367 public: 368 // Constructs an empty set. 369 explicit ZoneSet(Zone* zone) 370 : std::set<K, Compare, ZoneAllocator<K>>(Compare(), 371 ZoneAllocator<K>(zone)) {} 372 }; 373 374 // A wrapper subclass for std::map to make it easy to construct one that uses 375 // a zone allocator. 376 // Used in regexp-bytecode-peephole.cc 377 template <typename K, typename V, typename Compare = std::less<K>> 378 class ZoneMap 379 : public std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>> { 380 public: 381 // Constructs an empty map. 382 explicit ZoneMap(Zone* zone) 383 : std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>>( 384 Compare(), ZoneAllocator<std::pair<const K, V>>(zone)) {} 385 }; 386 387 // A wrapper subclass for std::unordered_map to make it easy to construct one 388 // that uses a zone allocator. 389 // Used in regexp-bytecode-peephole.cc 390 template <typename K, typename V, typename Hash = std::hash<K>, 391 typename KeyEqual = std::equal_to<K>> 392 class ZoneUnorderedMap 393 : public std::unordered_map<K, V, Hash, KeyEqual, 394 ZoneAllocator<std::pair<const K, V>>> { 395 public: 396 // Constructs an empty map. 397 explicit ZoneUnorderedMap(Zone* zone, size_t bucket_count = 100) 398 : std::unordered_map<K, V, Hash, KeyEqual, 399 ZoneAllocator<std::pair<const K, V>>>( 400 bucket_count, Hash(), KeyEqual(), 401 ZoneAllocator<std::pair<const K, V>>(zone)) {} 402 }; 403 404 // A wrapper subclass for base::SmallVector to make it easy to construct one 405 // that uses a zone allocator. 406 template <typename T, size_t kSize> 407 class SmallZoneVector : public base::SmallVector<T, kSize, ZoneAllocator<T>> { 408 public: 409 // Constructs an empty small vector. 410 explicit SmallZoneVector(Zone* zone) 411 : base::SmallVector<T, kSize, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {} 412 413 explicit SmallZoneVector(size_t size, Zone* zone) 414 : base::SmallVector<T, kSize, ZoneAllocator<T>>( 415 size, ZoneAllocator<T>(ZoneAllocator<T>(zone))) {} 416 }; 417 418 } // namespace internal 419 } // namespace v8 420 421 #endif // V8_UTIL_FLAG_H_