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/globals.h"
9#include "src/v8memory.h"
10
11namespace v8 {
12namespace internal {
13
14class Object;
15
16template <typename Subclass, typename Data,
17 size_t SlotDataAlignment = sizeof(Data)>
18class 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
69 void* ToVoidPtr() const { return reinterpret_cast<void*>(address()); }
70
71 Address address() const { return ptr_; }
72 // For symmetry with Handle.
73 TData* location() const { return reinterpret_cast<TData*>(ptr_); }
74
75 protected:
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().
91class 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
99 FullObjectSlot() : SlotBase(kNullAddress) {}
100 explicit FullObjectSlot(Address ptr) : SlotBase(ptr) {}
101 explicit FullObjectSlot(const Address* ptr)
102 : SlotBase(reinterpret_cast<Address>(ptr)) {}
103 inline explicit FullObjectSlot(Object* object);
104 template <typename T>
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 const Object operator*() const;
113 inline void store(Object value) const;
114
115 inline Object Acquire_Load() const;
116 inline Object Relaxed_Load() const;
117 inline void Relaxed_Store(Object value) const;
118 inline void Release_Store(Object value) const;
119 inline Object Release_CompareAndSwap(Object old, Object target) const;
120};
121
122// A FullMaybeObjectSlot instance describes a kSystemPointerSize-sized field
123// ("slot") holding a possibly-weak tagged pointer (think: MaybeObject).
124// Its address() is the address of the slot.
125// The slot's contents can be read and written using operator* and store().
126class FullMaybeObjectSlot
127 : public SlotBase<FullMaybeObjectSlot, Address, kSystemPointerSize> {
128 public:
129 using TObject = MaybeObject;
130 using THeapObjectSlot = FullHeapObjectSlot;
131
132 // Tagged value stored in this slot can be a weak pointer.
133 static constexpr bool kCanBeWeak = true;
134
135 FullMaybeObjectSlot() : SlotBase(kNullAddress) {}
136 explicit FullMaybeObjectSlot(Address ptr) : SlotBase(ptr) {}
137 explicit FullMaybeObjectSlot(Object* ptr)
138 : SlotBase(reinterpret_cast<Address>(ptr)) {}
139 explicit FullMaybeObjectSlot(MaybeObject* ptr)
140 : SlotBase(reinterpret_cast<Address>(ptr)) {}
141 template <typename T>
142 explicit FullMaybeObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot)
143 : SlotBase(slot.address()) {}
144
145 inline const MaybeObject operator*() const;
146 inline void store(MaybeObject value) const;
147
148 inline MaybeObject Relaxed_Load() const;
149 inline void Relaxed_Store(MaybeObject value) const;
150 inline void Release_CompareAndSwap(MaybeObject old, MaybeObject target) const;
151};
152
153// A FullHeapObjectSlot instance describes a kSystemPointerSize-sized field
154// ("slot") holding a weak or strong pointer to a heap object (think:
155// HeapObjectReference).
156// Its address() is the address of the slot.
157// The slot's contents can be read and written using operator* and store().
158// In case it is known that that slot contains a strong heap object pointer,
159// ToHeapObject() can be used to retrieve that heap object.
160class FullHeapObjectSlot : public SlotBase<FullHeapObjectSlot, Address> {
161 public:
162 FullHeapObjectSlot() : SlotBase(kNullAddress) {}
163 explicit FullHeapObjectSlot(Address ptr) : SlotBase(ptr) {}
164 explicit FullHeapObjectSlot(Object* ptr)
165 : SlotBase(reinterpret_cast<Address>(ptr)) {}
166 template <typename T>
167 explicit FullHeapObjectSlot(SlotBase<T, TData, kSlotDataAlignment> slot)
168 : SlotBase(slot.address()) {}
169
170 inline const HeapObjectReference operator*() const;
171 inline void store(HeapObjectReference value) const;
172
173 inline HeapObject ToHeapObject() const;
174
175 inline void StoreHeapObject(HeapObject value) const;
176};
177
178// TODO(ishell, v8:8875): When pointer compression is enabled the [u]intptr_t
179// and double fields are only kTaggedSize aligned so in order to avoid undefined
180// behavior in C++ code we use this iterator adaptor when using STL algorithms
181// with unaligned pointers.
182// It will be removed once all v8:8875 is fixed and all the full pointer and
183// double values in compressed V8 heap are properly aligned.
184template <typename T>
185class UnalignedSlot : public SlotBase<UnalignedSlot<T>, T, 1> {
186 public:
187 // This class is a stand-in for "T&" that uses custom read/write operations
188 // for the actual memory accesses.
189 class Reference {
190 public:
191 explicit Reference(Address address) : address_(address) {}
192 Reference(const Reference&) V8_NOEXCEPT = default;
193
194 Reference& operator=(const Reference& other) V8_NOEXCEPT {
195 WriteUnalignedValue<T>(address_, other.value());
196 return *this;
197 }
198 Reference& operator=(T value) {
199 WriteUnalignedValue<T>(address_, value);
200 return *this;
201 }
202
203 // Values of type UnalignedSlot::reference must be implicitly convertible
204 // to UnalignedSlot::value_type.
205 operator T() const { return value(); }
206
207 void swap(Reference& other) {
208 T tmp = value();
209 WriteUnalignedValue<T>(address_, other.value());
210 WriteUnalignedValue<T>(other.address_, tmp);
211 }
212
213 bool operator<(const Reference& other) const {
214 return value() < other.value();
215 }
216
217 bool operator==(const Reference& other) const {
218 return value() == other.value();
219 }
220
221 private:
222 T value() const { return ReadUnalignedValue<T>(address_); }
223
224 Address address_;
225 };
226
227 // The rest of this class follows C++'s "RandomAccessIterator" requirements.
228 // Most of the heavy lifting is inherited from SlotBase.
229 using difference_type = int;
230 using value_type = T;
231 using reference = Reference;
232 using pointer = T*;
233 using iterator_category = std::random_access_iterator_tag;
234
235 UnalignedSlot() : SlotBase<UnalignedSlot<T>, T, 1>(kNullAddress) {}
236 explicit UnalignedSlot(Address address)
237 : SlotBase<UnalignedSlot<T>, T, 1>(address) {}
238 explicit UnalignedSlot(T* address)
239 : SlotBase<UnalignedSlot<T>, T, 1>(reinterpret_cast<Address>(address)) {}
240
241 Reference operator*() const {
242 return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address());
243 }
244 Reference operator[](difference_type i) const {
245 return Reference(SlotBase<UnalignedSlot<T>, T, 1>::address() +
246 i * sizeof(T));
247 }
248
249 friend void swap(Reference lhs, Reference rhs) { lhs.swap(rhs); }
250
251 friend difference_type operator-(UnalignedSlot a, UnalignedSlot b) {
252 return static_cast<int>(a.address() - b.address()) / sizeof(T);
253 }
254};
255
256} // namespace internal
257} // namespace v8
258
259#endif // V8_OBJECTS_SLOTS_H_
260