1/*
2 * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
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 <type_traits>
29#include <wtf/StdLibExtras.h>
30
31namespace WTF {
32
33// The goal of this class is folding a pointer and 1 byte value into 8 bytes in both 32bit and 64bit architectures.
34// 32bit architecture just has a pair of byte and pointer, which should be 8 bytes.
35// In 64bit, we use the upper 5 bits and lower 3 bits (zero due to alignment) since these bits are safe to use even
36// with 5-level page tables where the effective pointer width is 57bits.
37template<typename PointerType, typename Type>
38class CompactPointerTuple {
39public:
40 static_assert(sizeof(Type) == 1, "");
41 static_assert(std::is_pointer<PointerType>::value, "");
42 static_assert(alignof(typename std::remove_pointer<PointerType>::type) >= alignof(void*), "");
43 static_assert(std::is_integral<Type>::value || std::is_enum<Type>::value, "");
44
45 CompactPointerTuple() = default;
46
47#if CPU(ADDRESS64)
48public:
49 static constexpr uint64_t encodeType(uint8_t type)
50 {
51 // Encode 8bit type UUUDDDDD into 64bit data DDDDD..56bit..UUU.
52 return (static_cast<uint64_t>(type) << 59) | (static_cast<uint64_t>(type) >> 5);
53 }
54 static constexpr uint8_t decodeType(uint64_t value)
55 {
56 // Decode 64bit data DDDDD..56bit..UUU into 8bit type UUUDDDDD.
57 return static_cast<uint8_t>((value >> 59) | (value << 5));
58 }
59
60 static constexpr uint64_t typeMask = encodeType(UINT8_MAX);
61 static_assert(0xF800000000000007ULL == typeMask, "");
62 static constexpr uint64_t pointerMask = ~typeMask;
63
64 CompactPointerTuple(PointerType pointer, Type type)
65 : m_data(bitwise_cast<uint64_t>(pointer) | encodeType(static_cast<uint8_t>(type)))
66 {
67 ASSERT((bitwise_cast<uint64_t>(pointer) & 0b111) == 0x0);
68 }
69
70 PointerType pointer() const { return bitwise_cast<PointerType>(m_data & pointerMask); }
71 void setPointer(PointerType pointer)
72 {
73 ASSERT((bitwise_cast<uint64_t>(pointer) & 0b111) == 0x0);
74 m_data = CompactPointerTuple(pointer, type()).m_data;
75 }
76
77 Type type() const { return static_cast<Type>(decodeType(m_data)); }
78 void setType(Type type)
79 {
80 m_data = CompactPointerTuple(pointer(), type).m_data;
81 }
82
83private:
84 uint64_t m_data { 0 };
85#else
86public:
87 CompactPointerTuple(PointerType pointer, Type type)
88 : m_pointer(pointer)
89 , m_type(type)
90 {
91 }
92
93 PointerType pointer() const { return m_pointer; }
94 void setPointer(PointerType pointer) { m_pointer = pointer; }
95 Type type() const { return m_type; }
96 void setType(Type type) { m_type = type; }
97
98private:
99 PointerType m_pointer { nullptr };
100 Type m_type { 0 };
101#endif
102};
103
104} // namespace WTF
105
106using WTF::CompactPointerTuple;
107