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 {
37 WTF_MAKE_FAST_ALLOCATED;
38public:
39 static constexpr bool isPackedType = true;
40
41 Packed()
42 : Packed(T { })
43 {
44 }
45
46 Packed(const T& value)
47 {
48 unalignedStore<T>(m_storage.data(), value);
49 }
50
51 T get() const
52 {
53 return unalignedLoad<T>(m_storage.data());
54 }
55
56 void set(const T& value)
57 {
58 unalignedStore<T>(m_storage.data(), value);
59 }
60
61 Packed<T>& operator=(const T& value)
62 {
63 set(value);
64 return *this;
65 }
66
67 template<class U>
68 T exchange(U&& newValue)
69 {
70 T oldValue = get();
71 set(std::forward<U>(newValue));
72 return oldValue;
73 }
74
75 void swap(Packed& other)
76 {
77 m_storage.swap(other.m_storage);
78 }
79
80 template<typename Other, typename = std::enable_if_t<Other::isPackedType>>
81 void swap(Other& other)
82 {
83 T t1 = get();
84 T t2 = other.get();
85 set(t2);
86 other.set(t1);
87 }
88
89 void swap(T& t2)
90 {
91 T t1 = get();
92 std::swap(t1, t2);
93 set(t1);
94 }
95
96private:
97 std::array<uint8_t, sizeof(T)> m_storage;
98};
99
100// PackedAlignedPtr can take alignment parameter too. PackedAlignedPtr only uses this alignment information if it is profitable: we use
101// 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,
102// we can use 4 bits in Darwin ARM64, we can compact cell pointer into 4 bytes (32 bits).
103template<typename T, size_t alignment = alignof(T)>
104class PackedAlignedPtr {
105 WTF_MAKE_FAST_ALLOCATED;
106public:
107 static_assert(hasOneBitSet(alignment), "Alignment needs to be power-of-two");
108 static constexpr bool isPackedType = true;
109 static constexpr unsigned alignmentShiftSizeIfProfitable = getLSBSetConstexpr(alignment);
110 static constexpr unsigned storageSizeWithoutAlignmentShift = roundUpToMultipleOf<8>(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH) / 8;
111 static constexpr unsigned storageSizeWithAlignmentShift = roundUpToMultipleOf<8>(WTF_CPU_EFFECTIVE_ADDRESS_WIDTH - alignmentShiftSizeIfProfitable) / 8;
112 static constexpr bool isAlignmentShiftProfitable = storageSizeWithoutAlignmentShift > storageSizeWithAlignmentShift;
113 static constexpr unsigned alignmentShiftSize = isAlignmentShiftProfitable ? alignmentShiftSizeIfProfitable : 0;
114 static constexpr unsigned storageSize = storageSizeWithAlignmentShift;
115
116 constexpr PackedAlignedPtr()
117 : m_storage()
118 {
119 }
120
121 constexpr PackedAlignedPtr(std::nullptr_t)
122 : m_storage()
123 {
124 }
125
126 PackedAlignedPtr(T* value)
127 {
128 set(value);
129 }
130
131 T* get() const
132 {
133 // FIXME: PackedPtr<> can load memory with one mov by checking page boundary.
134 // https://bugs.webkit.org/show_bug.cgi?id=197754
135 uintptr_t value = 0;
136#if CPU(LITTLE_ENDIAN)
137 memcpy(&value, m_storage.data(), storageSize);
138#else
139 memcpy(bitwise_cast<uint8_t*>(&value) + (sizeof(void*) - storageSize), m_storage.data(), storageSize);
140#endif
141 if (isAlignmentShiftProfitable)
142 value <<= alignmentShiftSize;
143 return bitwise_cast<T*>(value);
144 }
145
146 void set(T* passedValue)
147 {
148 uintptr_t value = bitwise_cast<uintptr_t>(passedValue);
149 if (isAlignmentShiftProfitable)
150 value >>= alignmentShiftSize;
151#if CPU(LITTLE_ENDIAN)
152 memcpy(m_storage.data(), &value, storageSize);
153#else
154 memcpy(m_storage.data(), bitwise_cast<uint8_t*>(&value) + (sizeof(void*) - storageSize), storageSize);
155#endif
156 }
157
158 void clear()
159 {
160 set(nullptr);
161 }
162
163 T* operator->() const { return get(); }
164 T& operator*() const { return *get(); }
165 bool operator!() const { return !get(); }
166 explicit operator bool() const { return get(); }
167
168 PackedAlignedPtr& operator=(T* value)
169 {
170 set(value);
171 return *this;
172 }
173
174 template<class U>
175 T exchange(U&& newValue)
176 {
177 T oldValue = get();
178 set(std::forward<U>(newValue));
179 return oldValue;
180 }
181
182 void swap(std::nullptr_t) { clear(); }
183
184 void swap(PackedAlignedPtr& other)
185 {
186 m_storage.swap(other.m_storage);
187 }
188
189 template<typename Other, typename = std::enable_if_t<Other::isPackedType>>
190 void swap(Other& other)
191 {
192 T t1 = get();
193 T t2 = other.get();
194 set(t2);
195 other.set(t1);
196 }
197
198 void swap(T& t2)
199 {
200 T t1 = get();
201 std::swap(t1, t2);
202 set(t1);
203 }
204
205private:
206 std::array<uint8_t, storageSize> m_storage;
207};
208
209template<typename T>
210class Packed<T*> : public PackedAlignedPtr<T, 1> {
211public:
212 using Base = PackedAlignedPtr<T, 1>;
213 using Base::Base;
214};
215
216template<typename T>
217using PackedPtr = Packed<T*>;
218
219template<typename T>
220struct PackedPtrTraits {
221 template<typename U> using RebindTraits = PackedPtrTraits<U>;
222
223 using StorageType = PackedPtr<T>;
224
225 template<class U> static ALWAYS_INLINE T* exchange(StorageType& ptr, U&& newValue) { return ptr.exchange(newValue); }
226
227 template<typename Other> static ALWAYS_INLINE void swap(PackedPtr<T>& a, Other& b) { a.swap(b); }
228
229 static ALWAYS_INLINE T* unwrap(const StorageType& ptr) { return ptr.get(); }
230};
231
232} // namespace WTF
233
234using WTF::Packed;
235using WTF::PackedAlignedPtr;
236using WTF::PackedPtr;
237