aboutsummaryrefslogtreecommitdiff
path: root/pw_alignment/public/pw_alignment/alignment.h
blob: e295c5d0c6d590aa17b4673830d2566b19b922f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 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/120998): Once this bug is addressed, this module can likely
// be removed and we could just inline the using statements.

#include <atomic>
#include <limits>
#include <type_traits>

namespace pw {

#if __cplusplus >= 202002L
using bit_ceil = 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

// The NaturallyAligned class is a wrapper class for ensuring the object is
// aligned to a power of 2 bytes greater than or equal to its size.
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){return T::operator=(other);
}  // namespace pw
}
;

// This is a convenience wrapper for ensuring the object held by std::atomic is
// naturally aligned. Ensuring the underlying objects's alignment is natural
// allows clang to replace libcalls to atomic functions
// (__atomic_load/store/exchange/etc) with native instructions when appropriate.
//
// Example usage:
//
//   // Here 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 instructions.
//   pw::AlignedAtomic<std::optional<bool>> mute_enable{};
//
template <typename T>
using AlignedAtomic = std::atomic<NaturallyAligned<T>>;

}  // namespace pw