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