blob: f1af80a88dca40f925e737d99809fdc93c2f1c9f [file] [log] [blame]
// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
// todo-check: ignore
// TODO(fxbug.dev/42072051): Once this bug is addressed, this module can likely
// be removed and we could just inline the using statements.
#include <atomic>
#if __cplusplus >= 202002L
#include <bit>
#endif // __cplusplus >= 202002L
#include <limits>
#include <type_traits>
namespace pw {
#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
using std::bit_ceil;
#else
constexpr size_t countl_zero(size_t x) noexcept {
size_t size_digits = std::numeric_limits<size_t>::digits;
if (sizeof(x) <= sizeof(unsigned int))
return __builtin_clz(static_cast<unsigned int>(x)) -
(std::numeric_limits<unsigned int>::digits - size_digits);
if (sizeof(x) <= sizeof(unsigned long))
return __builtin_clzl(static_cast<unsigned long>(x)) -
(std::numeric_limits<unsigned long>::digits - size_digits);
static_assert(sizeof(x) <= sizeof(unsigned long long));
return __builtin_clzll(static_cast<unsigned long long>(x)) -
(std::numeric_limits<unsigned long long>::digits - size_digits);
}
constexpr size_t bit_width(size_t x) noexcept {
return std::numeric_limits<size_t>::digits - countl_zero(x);
}
constexpr size_t bit_ceil(size_t x) noexcept {
if (x == 0)
return 1;
return size_t{1} << bit_width(size_t{x - 1});
}
#endif // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
/// @defgroup pw_alignment
/// @ingroup pw_alignment
///
/// Ensures the object is naturally aligned to a power of 2 bytes greater
/// than or equal to its size. `NaturallyAligned` is a wrapper class.
///
/// Example:
///
/// @code{.cpp}
/// #include "pw_alignment/alignment.h"
///
/// std::atomic<pw::NaturallyAligned<std::optional<bool>>> nat_aligned_obj;
/// @endcode
template <typename T>
struct [[gnu::aligned(bit_ceil(sizeof(T)))]] NaturallyAligned : public T {
NaturallyAligned() : T() {}
NaturallyAligned(const T& t) : T(t) {}
template <class U>
NaturallyAligned(const U& u) : T(u) {}
NaturallyAligned& operator=(T other) {
T::operator=(other);
return *this;
} // namespace pw
};
/// @ingroup pw_alignment
///
/// Ensures the object held by `std::atomic` is naturally aligned. This
/// enables the compiler to replace libcalls to atomic functions with native
/// instructions when appropriate. `AlignedAtomic` is a convenience wrapper.
///
/// Example:
///
/// @code{.cpp}
/// #include "pw_alignment/alignment.h"
///
/// pw::AlignedAtomic<std::optional<bool>> mute_enable{};
/// @endcode
///
/// `std::optional<bool>` has a size of 2 but alignment of 1, which would
/// normally lower to an `__atomic_*` libcall, but `pw::NaturallyAligned` in
/// `std::atomic` tells the compiler to align the object to 2 bytes, which
/// satisfies the requirements for replacing `__atomic_*` with native
/// instructions.
template <typename T>
using AlignedAtomic = std::atomic<NaturallyAligned<T>>;
} // namespace pw