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