1/*
2 * Copyright (C) 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <array>
29#include <wtf/MathExtras.h>
30#include <wtf/StdLibExtras.h>
31#include <wtf/UnalignedAccess.h>
32
33namespace WTF {
34
35template<typename T>
36class Packed {
37public:
38 static constexpr bool isPackedType = true;
39
40 Packed()
41 : Packed(T { })
42 {
43 }
44
45 Packed(const T& value)
46 {
47 unalignedStore<T>(m_storage.data(), value);
48 }
49
50 T get() const
51 {
52 return unalignedLoad<T>(m_storage.data());
53 }
54
55 void set(const T& value)
56 {
57 unalignedStore<T>(m_storage.data(), value);
58 }
59
60 Packed<T>& operator=(const T& value)
61 {
62 set(value);
63 return *this;
64 }
65
66 template<class U>
67 T exchange(U&& newValue)
68 {
69 T oldValue = get();
70 set(std::forward<U>(newValue));
71 return oldValue;
72 }
73
74 void swap(Packed& other)
75 {
76 m_storage.swap(other.m_storage);
77 }
78
79 template<typename Other, typename = std::enable_if_t<Other::isPackedType>>
80 void swap(Other& other)
81 {
82 T t1 = get();
83 T t2 = other.get();
84 set(t2);
85 other.set(t1);
86 }
87
88 void swap(T& t2)
89 {
90 T t1 = get();
91 std::swap(t1, t2);
92 set(t1);
93 }
94
95private:
96 std::array<uint8_t, sizeof(T)> m_storage;
97};
98
99// PackedAlignedPtr can take alignment parameter too. PackedAlignedPtr only uses this alignment information if it is profitable: we use
100// alignment information only when we can reduce the size of the storage. Since the pointer width is 36 bits and JSCells are aligned to 16 bytes,
101// we can use 4 bits in Darwin ARM64, we can compact cell pointer into 4 bytes (32 bits).
102template<typename T, size_t alignment = alignof(T)>
103class PackedAlignedPtr {
104public:
105 static_assert(hasOneBitSet(alignment), "Alignment needs to be power-of-two");
106 static constexpr bool isPackedType = true;
107 static constexpr unsigned alignmentShiftSizeIfProfitable = getLSBSetConstexpr(alignment);
108 static constexpr unsigned storageSizeWithoutAlignmentShift = roundUpToMultipleOf<8>(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH) / 8;
109 static constexpr unsigned storageSizeWithAlignmentShift = roundUpToMultipleOf<8>(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH - alignmentShiftSizeIfProfitable) / 8;
110 static constexpr bool isAlignmentShiftProfitable = storageSizeWithoutAlignmentShift > storageSizeWithAlignmentShift;
111 static constexpr unsigned alignmentShiftSize = isAlignmentShiftProfitable ? alignmentShiftSizeIfProfitable : 0;
112 static constexpr unsigned storageSize = storageSizeWithAlignmentShift;
113
114 constexpr PackedAlignedPtr()
115 : m_storage()
116 {
117 }
118
119 constexpr PackedAlignedPtr(std::nullptr_t)
120 : m_storage()
121 {
122 }
123
124 PackedAlignedPtr(T* value)
125 {
126 set(value);
127 }
128
129 T* get() const
130 {
131 // FIXME: PackedPtr<> can load memory with one mov by checking page boundary.
132 // https://bugs.webkit.org/show_bug.cgi?id=197754
133 uintptr_t value = 0;
134#if CPU(LITTLE_ENDIAN)
135 memcpy(&value, m_storage.data(), storageSize);
136#else
137 memcpy(bitwise_cast<uint8_t*>(&value) + (sizeof(void*) - storageSize), m_storage.data(), storageSize);
138#endif
139 if (isAlignmentShiftProfitable)
140 value <<= alignmentShiftSize;
141 return bitwise_cast<T*>(value);
142 }
143
144 void set(T* passedValue)
145 {
146 uintptr_t value = bitwise_cast<uintptr_t>(passedValue);
147 if (isAlignmentShiftProfitable)
148 value >>= alignmentShiftSize;
149#if CPU(LITTLE_ENDIAN)
150 memcpy(m_storage.data(), &value, storageSize);
151#else
152 memcpy(m_storage.data(), bitwise_cast<uint8_t*>(&value) + (sizeof(void*) - storageSize), storageSize);
153#endif
154 }
155
156 void clear()
157 {
158 set(nullptr);
159 }
160
161 T* operator->() const { return get(); }
162 T& operator*() const { return *get(); }
163 bool operator!() const { return !get(); }
164 explicit operator bool() const { return get(); }
165
166 PackedAlignedPtr& operator=(T* value)
167 {
168 set(value);
169 return *this;
170 }
171
172 template<class U>
173 T exchange(U&& newValue)
174 {
175 T oldValue = get();
176 set(std::forward<U>(newValue));
177 return oldValue;
178 }
179
180 void swap(std::nullptr_t) { clear(); }
181
182 void swap(PackedAlignedPtr& other)
183 {
184 m_storage.swap(other.m_storage);
185 }
186
187 template<typename Other, typename = std::enable_if_t<Other::isPackedType>>
188 void swap(Other& other)
189 {
190 T t1 = get();
191 T t2 = other.get();
192 set(t2);
193 other.set(t1);
194 }
195
196 void swap(T& t2)
197 {
198 T t1 = get();
199 std::swap(t1, t2);
200 set(t1);
201 }
202
203private:
204 std::array<uint8_t, storageSize> m_storage;
205};
206
207template<typename T>
208class Packed<T*> : public PackedAlignedPtr<T, 1> {
209public:
210 using Base = PackedAlignedPtr<T, 1>;
211 using Base::Base;
212};
213
214template<typename T>
215using PackedPtr = Packed<T*>;
216
217template<typename T>
218struct PackedPtrTraits {
219 template<typename U> using RebindTraits = PackedPtrTraits<U>;
220
221 using StorageType = PackedPtr<T>;
222
223 template<class U> static ALWAYS_INLINE T* exchange(StorageType& ptr, U&& newValue) { return ptr.exchange(newValue); }
224
225 template<typename Other> static ALWAYS_INLINE void swap(PackedPtr<T>& a, Other& b) { a.swap(b); }
226
227 static ALWAYS_INLINE T* unwrap(const StorageType& ptr) { return ptr.get(); }
228};
229
230} // namespace WTF
231
232using WTF::Packed;
233using WTF::PackedAlignedPtr;
234using WTF::PackedPtr;
235