1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP___MEMORY_COMPRESSED_PAIR_H
11#define _LIBCPP___MEMORY_COMPRESSED_PAIR_H
12
13#include <__config>
14#include <__cstddef/size_t.h>
15#include <__type_traits/datasizeof.h>
16#include <__type_traits/is_empty.h>
17#include <__type_traits/is_final.h>
18
19#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
20# pragma GCC system_header
21#endif
22
23// ================================================================================================================== //
24// The utilites here are for staying ABI compatible with the legacy `__compressed_pair`. They should not be used //
25// for new data structures. Use `_LIBCPP_NO_UNIQUE_ADDRESS` for new data structures instead (but make sure you //
26// understand how it works). //
27// ================================================================================================================== //
28
29// On GCC, the first member is aligned to the alignment of the second member to force padding in front of the compressed
30// pair in case there are members before it.
31//
32// For example:
33// (assuming x86-64 linux)
34// class SomeClass {
35// uint32_t member1;
36// _LIBCPP_COMPRESSED_PAIR(uint32_t, member2, uint64_t, member3);
37// }
38//
39// The layout with __compressed_pair is:
40// member1 - offset: 0, size: 4
41// padding - offset: 4, size: 4
42// member2 - offset: 8, size: 4
43// padding - offset: 12, size: 4
44// member3 - offset: 16, size: 8
45//
46// If the [[gnu::aligned]] wasn't there, the layout would instead be:
47// member1 - offset: 0, size: 4
48// member2 - offset: 4, size: 4
49// member3 - offset: 8, size: 8
50//
51// Furthermore, that alignment must be the same as what was used in the old __compressed_pair layout, so we must
52// handle reference types specially since alignof(T&) == alignof(T).
53// See https://llvm.org/PR118559.
54//
55// On Clang, this is unnecessary, since we use anonymous structs instead, which automatically handle the alignment
56// correctly.
57
58#ifndef _LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING
59
60_LIBCPP_BEGIN_NAMESPACE_STD
61
62template <class _Tp>
63inline const size_t __compressed_pair_alignment = _LIBCPP_ALIGNOF(_Tp);
64
65template <class _Tp>
66inline const size_t __compressed_pair_alignment<_Tp&> = _LIBCPP_ALIGNOF(void*);
67
68template <class _ToPad>
69inline const bool __is_reference_or_unpadded_object =
70 (is_empty<_ToPad>::value && !__is_final_v<_ToPad>) || sizeof(_ToPad) == __datasizeof_v<_ToPad>;
71
72template <class _Tp>
73inline const bool __is_reference_or_unpadded_object<_Tp&> = true;
74
75template <class _Tp>
76inline const bool __is_reference_or_unpadded_object<_Tp&&> = true;
77
78template <class _ToPad, bool _Empty = __is_reference_or_unpadded_object<_ToPad> >
79class __compressed_pair_padding {
80 char __padding_[sizeof(_ToPad) - __datasizeof_v<_ToPad>] = {};
81};
82
83template <class _ToPad>
84class __compressed_pair_padding<_ToPad, true> {};
85
86# define _LIBCPP_COMPRESSED_ELEMENT(T1, Initializer1) \
87 _LIBCPP_NO_UNIQUE_ADDRESS T1 Initializer1; \
88 _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T1> _LIBCPP_CONCAT3(__padding_, __LINE__, _)
89
90// TODO: Fix the ABI for GCC as well once https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121637 is fixed
91# ifdef _LIBCPP_COMPILER_GCC
92# define _LIBCPP_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
93 _LIBCPP_NO_UNIQUE_ADDRESS __attribute__((__aligned__(::std::__compressed_pair_alignment<T2>))) T1 Initializer1; \
94 _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T1> _LIBCPP_CONCAT3(__padding1_, __LINE__, _); \
95 _LIBCPP_NO_UNIQUE_ADDRESS T2 Initializer2; \
96 _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T2> _LIBCPP_CONCAT3(__padding2_, __LINE__, _)
97# else
98# define _LIBCPP_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
99 struct { \
100 _LIBCPP_NO_UNIQUE_ADDRESS T1 Initializer1; \
101 _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T1> _LIBCPP_CONCAT3(__padding1_, __LINE__, _); \
102 _LIBCPP_NO_UNIQUE_ADDRESS T2 Initializer2; \
103 _LIBCPP_NO_UNIQUE_ADDRESS ::std::__compressed_pair_padding<T2> _LIBCPP_CONCAT3(__padding2_, __LINE__, _); \
104 }
105# endif
106
107_LIBCPP_END_NAMESPACE_STD
108
109#else
110# define _LIBCPP_COMPRESSED_ELEMENT(T1, Initializer1) _LIBCPP_NO_UNIQUE_ADDRESS T1 Initializer1
111
112# define _LIBCPP_COMPRESSED_PAIR(T1, Name1, T2, Name2) \
113 _LIBCPP_NO_UNIQUE_ADDRESS T1 Name1; \
114 _LIBCPP_NO_UNIQUE_ADDRESS T2 Name2
115#endif // _LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING
116
117#endif // _LIBCPP___MEMORY_COMPRESSED_PAIR_H
118