xref: /aosp_15_r20/external/pigweed/pw_alignment/public/pw_alignment/alignment.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 // todo-check: ignore
17 // TODO(fxbug.dev/42072051): Once this bug is addressed, this module can likely
18 // be removed and we could just inline the using statements.
19 
20 #include <atomic>
21 #if __cplusplus >= 202002L
22 #include <bit>
23 #endif  // __cplusplus >= 202002L
24 #include <limits>
25 #include <type_traits>
26 
27 namespace pw {
28 
29 #if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
30 using std::bit_ceil;
31 #else
32 constexpr size_t countl_zero(size_t x) noexcept {
33   size_t size_digits = std::numeric_limits<size_t>::digits;
34 
35   if (sizeof(x) <= sizeof(unsigned int))
36     return __builtin_clz(static_cast<unsigned int>(x)) -
37            (std::numeric_limits<unsigned int>::digits - size_digits);
38 
39   if (sizeof(x) <= sizeof(unsigned long))
40     return __builtin_clzl(static_cast<unsigned long>(x)) -
41            (std::numeric_limits<unsigned long>::digits - size_digits);
42 
43   static_assert(sizeof(x) <= sizeof(unsigned long long));
44   return __builtin_clzll(static_cast<unsigned long long>(x)) -
45          (std::numeric_limits<unsigned long long>::digits - size_digits);
46 }
47 
48 constexpr size_t bit_width(size_t x) noexcept {
49   return std::numeric_limits<size_t>::digits - countl_zero(x);
50 }
51 
52 constexpr size_t bit_ceil(size_t x) noexcept {
53   if (x == 0)
54     return 1;
55   return size_t{1} << bit_width(size_t{x - 1});
56 }
57 #endif  // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
58 
59 /// @defgroup pw_alignment
60 
61 /// @ingroup pw_alignment
62 ///
63 /// Ensures the object is naturally aligned to a power of 2 bytes greater
64 /// than or equal to its size. `NaturallyAligned` is a wrapper class.
65 ///
66 /// Example:
67 ///
68 /// @code{.cpp}
69 ///   #include "pw_alignment/alignment.h"
70 ///
71 ///   std::atomic<pw::NaturallyAligned<std::optional<bool>>> nat_aligned_obj;
72 /// @endcode
73 template <typename T>
74 struct [[gnu::aligned(bit_ceil(sizeof(T)))]] NaturallyAligned : public T {
NaturallyAlignedNaturallyAligned75   NaturallyAligned() : T() {}
NaturallyAlignedNaturallyAligned76   NaturallyAligned(const T& t) : T(t) {}
77   template <class U>
NaturallyAlignedNaturallyAligned78   NaturallyAligned(const U& u) : T(u) {}
79   NaturallyAligned& operator=(T other) {
80     T::operator=(other);
81     return *this;
82   }  // namespace pw
83 };
84 
85 /// @ingroup pw_alignment
86 ///
87 /// Ensures the object held by `std::atomic` is naturally aligned. This
88 /// enables the compiler to replace libcalls to atomic functions with native
89 /// instructions when appropriate. `AlignedAtomic` is a convenience wrapper.
90 ///
91 /// Example:
92 ///
93 /// @code{.cpp}
94 ///   #include "pw_alignment/alignment.h"
95 ///
96 ///   pw::AlignedAtomic<std::optional<bool>> mute_enable{};
97 /// @endcode
98 ///
99 /// `std::optional<bool>` has a size of 2 but alignment of 1, which would
100 /// normally lower to an `__atomic_*` libcall, but `pw::NaturallyAligned` in
101 /// `std::atomic` tells the compiler to align the object to 2 bytes, which
102 /// satisfies the requirements for replacing `__atomic_*` with native
103 /// instructions.
104 template <typename T>
105 using AlignedAtomic = std::atomic<NaturallyAligned<T>>;
106 
107 }  // namespace pw
108