1 // Copyright 2018 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_OBJECTS_SLOTS_H_ 6 #define V8_OBJECTS_SLOTS_H_ 7 8 #include "src/base/memory.h" 9 #include "src/common/globals.h" 10 11 namespace v8 { 12 namespace internal { 13 14 class Object; 15 16 template <typename Subclass, typename Data, 17 size_t SlotDataAlignment = sizeof(Data)> 18 class SlotBase { 19 public: 20 using TData = Data; 21 22 static constexpr size_t kSlotDataSize = sizeof(Data); 23 static constexpr size_t kSlotDataAlignment = SlotDataAlignment; 24 25 Subclass& operator++() { // Prefix increment. 26 ptr_ += kSlotDataSize; 27 return *static_cast<Subclass*>(this); 28 } 29 Subclass operator++(int) { // Postfix increment. 30 Subclass result = *static_cast<Subclass*>(this); 31 ptr_ += kSlotDataSize; 32 return result; 33 } 34 Subclass& operator--() { // Prefix decrement. 35 ptr_ -= kSlotDataSize; 36 return *static_cast<Subclass*>(this); 37 } 38 Subclass operator--(int) { // Postfix decrement. 39 Subclass result = *static_cast<Subclass*>(this); 40 ptr_ -= kSlotDataSize; 41 return result; 42 } 43 44 bool operator<(const SlotBase& other) const { return ptr_ < other.ptr_; } 45 bool operator<=(const SlotBase& other) const { return ptr_ <= other.ptr_; } 46 bool operator>(const SlotBase& other) const { return ptr_ > other.ptr_; } 47 bool operator>=(const SlotBase& other) const { return ptr_ >= other.ptr_; } 48 bool operator==(const SlotBase& other) const { return ptr_ == other.ptr_; } 49 bool operator!=(const SlotBase& other) const { return ptr_ != other.ptr_; } 50 size_t operator-(const SlotBase& other) const { 51 DCHECK_GE(ptr_, other.ptr_); 52 return static_cast<size_t>((ptr_ - other.ptr_) / kSlotDataSize); 53 } 54 Subclass operator-(int i) const { return Subclass(ptr_ - i * kSlotDataSize); } 55 Subclass operator+(int i) const { return Subclass(ptr_ + i * kSlotDataSize); } 56 friend Subclass operator+(int i, const Subclass& slot) { 57 return Subclass(slot.ptr_ + i * kSlotDataSize); 58 } 59 Subclass& operator+=(int i) { 60 ptr_ += i * kSlotDataSize; 61 return *static_cast<Subclass*>(this); 62 } 63 Subclass operator-(int i) { return Subclass(ptr_ - i * kSlotDataSize); } 64 Subclass& operator-=(int i) { 65 ptr_ -= i * kSlotDataSize; 66 return *static_cast<Subclass*>(this); 67 } 68 ToVoidPtr()69 void* ToVoidPtr() const { return reinterpret_cast<void*>(address()); } 70 address()71 Address address() const { return ptr_; } 72 // For symmetry with Handle. location()73 TData* location() const { return reinterpret_cast<TData*>(ptr_); } 74 75 protected: SlotBase(Address ptr)76 explicit SlotBase(Address ptr) : ptr_(ptr) { 77 DCHECK(IsAligned(ptr, kSlotDataAlignment)); 78 } 79 80 private: 81 // This field usually describes an on-heap address (a slot within an object), 82 // so its type should not be a pointer to another C++ wrapper class. 83 // Type safety is provided by well-defined conversion operations. 84 Address ptr_; 85 }; 86 87 // An FullObjectSlot instance describes a kSystemPointerSize-sized field 88 // ("slot") holding a tagged pointer (smi or strong heap object). 89 // Its address() is the address of the slot. 90 // The slot's contents can be read and written using operator* and store(). 91 class FullObjectSlot : public SlotBase<FullObjectSlot, Address> { 92 public: 93 using TObject = Object; 94 using THeapObjectSlot = FullHeapObjectSlot; 95 96 // Tagged value stored in this slot is guaranteed to never be a weak pointer. 97 static constexpr bool kCanBeWeak = false; 98 FullObjectSlot()99 FullObjectSlot() : SlotBase(kNullAddress) {} FullObjectSlot(Address ptr)100 explicit FullObjectSlot(Address ptr) : SlotBase(ptr) {} FullObjectSlot(const Address * ptr)101 explicit FullObjectSlot(const Address* ptr) 102 : SlotBase(reinterpret_cast<Address>(ptr)) {} 103 inline explicit FullObjectSlot(Object* object); 104 template <typename T> FullObjectSlot(SlotBase<T,TData,kSlotDataAlignment> slot)105 explicit FullObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) 106 : SlotBase(slot.address()) {} 107 108 // Compares memory representation of a value stored in the slot with given 109 // raw value. 110 inline bool contains_value(Address raw_value) const; 111 112 inline Object operator*() const; 113 inline Object load(IsolateRoot isolate) const; 114 inline void store(Object value) const; 115 116 inline Object Acquire_Load() const; 117 inline Object Acquire_Load(IsolateRoot isolate) const; 118 inline Object Relaxed_Load() const; 119 inline Object Relaxed_Load(IsolateRoot isolate) const; 120 inline void Relaxed_Store(Object value) const; 121 inline void Release_Store(Object value) const; 122 inline Object Relaxed_CompareAndSwap(Object old, Object target) const; 123 inline Object Release_CompareAndSwap(Object old, Object target) const; 124 }; 125 126 // A FullMaybeObjectSlot instance describes a kSystemPointerSize-sized field 127 // ("slot") holding a possibly-weak tagged pointer (think: MaybeObject). 128 // Its address() is the address of the slot. 129 // The slot's contents can be read and written using operator* and store(). 130 class FullMaybeObjectSlot 131 : public SlotBase<FullMaybeObjectSlot, Address, kSystemPointerSize> { 132 public: 133 using TObject = MaybeObject; 134 using THeapObjectSlot = FullHeapObjectSlot; 135 136 // Tagged value stored in this slot can be a weak pointer. 137 static constexpr bool kCanBeWeak = true; 138 FullMaybeObjectSlot()139 FullMaybeObjectSlot() : SlotBase(kNullAddress) {} FullMaybeObjectSlot(Address ptr)140 explicit FullMaybeObjectSlot(Address ptr) : SlotBase(ptr) {} FullMaybeObjectSlot(Object * ptr)141 explicit FullMaybeObjectSlot(Object* ptr) 142 : SlotBase(reinterpret_cast<Address>(ptr)) {} FullMaybeObjectSlot(MaybeObject * ptr)143 explicit FullMaybeObjectSlot(MaybeObject* ptr) 144 : SlotBase(reinterpret_cast<Address>(ptr)) {} 145 template <typename T> FullMaybeObjectSlot(SlotBase<T,TData,kSlotDataAlignment> slot)146 explicit FullMaybeObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) 147 : SlotBase(slot.address()) {} 148 149 inline MaybeObject operator*() const; 150 inline MaybeObject load(IsolateRoot isolate) const; 151 inline void store(MaybeObject value) const; 152 153 inline MaybeObject Relaxed_Load() const; 154 inline MaybeObject Relaxed_Load(IsolateRoot isolate) const; 155 inline void Relaxed_Store(MaybeObject value) const; 156 inline void Release_CompareAndSwap(MaybeObject old, MaybeObject target) const; 157 }; 158 159 // A FullHeapObjectSlot instance describes a kSystemPointerSize-sized field 160 // ("slot") holding a weak or strong pointer to a heap object (think: 161 // HeapObjectReference). 162 // Its address() is the address of the slot. 163 // The slot's contents can be read and written using operator* and store(). 164 // In case it is known that that slot contains a strong heap object pointer, 165 // ToHeapObject() can be used to retrieve that heap object. 166 class FullHeapObjectSlot : public SlotBase<FullHeapObjectSlot, Address> { 167 public: FullHeapObjectSlot()168 FullHeapObjectSlot() : SlotBase(kNullAddress) {} FullHeapObjectSlot(Address ptr)169 explicit FullHeapObjectSlot(Address ptr) : SlotBase(ptr) {} FullHeapObjectSlot(Object * ptr)170 explicit FullHeapObjectSlot(Object* ptr) 171 : SlotBase(reinterpret_cast<Address>(ptr)) {} 172 template <typename T> FullHeapObjectSlot(SlotBase<T,TData,kSlotDataAlignment> slot)173 explicit FullHeapObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot) 174 : SlotBase(slot.address()) {} 175 176 inline HeapObjectReference operator*() const; 177 inline HeapObjectReference load(IsolateRoot isolate) const; 178 inline void store(HeapObjectReference value) const; 179 180 inline HeapObject ToHeapObject() const; 181 182 inline void StoreHeapObject(HeapObject value) const; 183 }; 184 185 // TODO(ishell, v8:8875): When pointer compression is enabled the [u]intptr_t 186 // and double fields are only kTaggedSize aligned so in order to avoid undefined 187 // behavior in C++ code we use this iterator adaptor when using STL algorithms 188 // with unaligned pointers. 189 // It will be removed once all v8:8875 is fixed and all the full pointer and 190 // double values in compressed V8 heap are properly aligned. 191 template <typename T> 192 class UnalignedSlot : public SlotBase<UnalignedSlot<T>, T, 1> { 193 public: 194 // This class is a stand-in for "T&" that uses custom read/write operations 195 // for the actual memory accesses. 196 class Reference { 197 public: Reference(Address address)198 explicit Reference(Address address) : address_(address) {} 199 Reference(const Reference&) V8_NOEXCEPT = default; 200 201 Reference& operator=(const Reference& other) V8_NOEXCEPT { 202 base::WriteUnalignedValue<T>(address_, other.value()); 203 return *this; 204 } 205 Reference& operator=(T value) { 206 base::WriteUnalignedValue<T>(address_, value); 207 return *this; 208 } 209 210 // Values of type UnalignedSlot::reference must be implicitly convertible 211 // to UnalignedSlot::value_type. T()212 operator T() const { return value(); } 213 swap(Reference & other)214 void swap(Reference& other) { 215 T tmp = value(); 216 base::WriteUnalignedValue<T>(address_, other.value()); 217 base::WriteUnalignedValue<T>(other.address_, tmp); 218 } 219 220 bool operator<(const Reference& other) const { 221 return value() < other.value(); 222 } 223 224 bool operator==(const Reference& other) const { 225 return value() == other.value(); 226 } 227 228 private: value()229 T value() const { return base::ReadUnalignedValue<T>(address_); } 230 231 Address address_; 232 }; 233 234 // The rest of this class follows C++'s "RandomAccessIterator" requirements. 235 // Most of the heavy lifting is inherited from SlotBase. 236 using difference_type = int; 237 using value_type = T; 238 using reference = Reference; 239 using pointer = T*; 240 using iterator_category = std::random_access_iterator_tag; 241 UnalignedSlot()242 UnalignedSlot() : SlotBase<UnalignedSlot<T>, T, 1>(kNullAddress) {} UnalignedSlot(Address address)243 explicit UnalignedSlot(Address address) 244 : SlotBase<UnalignedSlot<T>, T, 1>(address) {} UnalignedSlot(T * address)245 explicit UnalignedSlot(T* address) 246 : SlotBase<UnalignedSlot<T>, T, 1>(reinterpret_cast<Address>(address)) {} 247 248 Reference operator*() const { 249 return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address()); 250 } 251 Reference operator[](difference_type i) const { 252 return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address() + 253 i * sizeof(T)); 254 } 255 swap(Reference lhs,Reference rhs)256 friend void swap(Reference lhs, Reference rhs) { lhs.swap(rhs); } 257 258 friend difference_type operator-(UnalignedSlot a, UnalignedSlot b) { 259 return static_cast<int>(a.address() - b.address()) / sizeof(T); 260 } 261 }; 262 263 // An off-heap uncompressed object slot can be the same as an on-heap one, with 264 // a few methods deleted. 265 class OffHeapFullObjectSlot : public FullObjectSlot { 266 public: OffHeapFullObjectSlot()267 OffHeapFullObjectSlot() : FullObjectSlot() {} OffHeapFullObjectSlot(const Address * ptr)268 explicit OffHeapFullObjectSlot(const Address* ptr) : FullObjectSlot(ptr) {} 269 270 inline Object operator*() const = delete; 271 272 using FullObjectSlot::Relaxed_Load; 273 inline Object Relaxed_Load() const = delete; 274 }; 275 276 } // namespace internal 277 } // namespace v8 278 279 #endif // V8_OBJECTS_SLOTS_H_ 280