AK: Make parts of Atomic constexpr

These new constexpr functions are for the most part identical to
the same non-constexpr functions, except they drop the `volatile`
qualifier on `this`.

`fetch_add()`, `fetch_sub()`, `load()` have explicit
`is_constant_evaluated()` branches that don't call (non-constexpr)
atomic built-ins. Off the constexpr path, they forward to the
volatile functions.
This commit is contained in:
Nico Weber 2024-12-19 21:38:38 -05:00
parent e3826bf791
commit 761320c4d2

View file

@ -247,11 +247,21 @@ public:
return fetch_add(1) + 1;
}
ALWAYS_INLINE constexpr T operator++() noexcept
{
return fetch_add(1) + 1;
}
ALWAYS_INLINE T operator++(int) volatile noexcept
{
return fetch_add(1);
}
ALWAYS_INLINE constexpr T operator++(int) noexcept
{
return fetch_add(1);
}
ALWAYS_INLINE T operator+=(T val) volatile noexcept
{
return fetch_add(val) + val;
@ -262,16 +272,36 @@ public:
return __atomic_fetch_add(&m_value, val, order);
}
ALWAYS_INLINE constexpr T fetch_add(T val, MemoryOrder order = DefaultMemoryOrder) noexcept
{
if (is_constant_evaluated()) {
T old_value = m_value;
m_value += val;
return old_value;
}
return static_cast<Atomic volatile*>(this)->fetch_add(val, order);
}
ALWAYS_INLINE T operator--() volatile noexcept
{
return fetch_sub(1) - 1;
}
ALWAYS_INLINE constexpr T operator--() noexcept
{
return fetch_sub(1) - 1;
}
ALWAYS_INLINE T operator--(int) volatile noexcept
{
return fetch_sub(1);
}
ALWAYS_INLINE constexpr T operator--(int) noexcept
{
return fetch_sub(1);
}
ALWAYS_INLINE T operator-=(T val) volatile noexcept
{
return fetch_sub(val) - val;
@ -288,6 +318,16 @@ public:
return __atomic_fetch_sub(ptr, val, order);
}
ALWAYS_INLINE constexpr T fetch_sub(T val, MemoryOrder order = DefaultMemoryOrder) noexcept
{
if (is_constant_evaluated()) {
T old_value = m_value;
m_value -= val;
return old_value;
}
return static_cast<Atomic volatile*>(this)->fetch_sub(val, order);
}
ALWAYS_INLINE T operator&=(T val) volatile noexcept
{
return fetch_and(val) & val;
@ -323,11 +363,23 @@ public:
return load();
}
ALWAYS_INLINE constexpr operator T() const noexcept
{
return load();
}
ALWAYS_INLINE T load(MemoryOrder order = DefaultMemoryOrder) const volatile noexcept
{
return __atomic_load_n(&m_value, order);
}
ALWAYS_INLINE constexpr T load(MemoryOrder order = DefaultMemoryOrder) const noexcept
{
if (is_constant_evaluated())
return m_value;
return static_cast<Atomic const volatile*>(this)->load(order);
}
// NOLINTNEXTLINE(misc-unconventional-assign-operator) We want operator= to exchange the value, so returning an object of type Atomic& here does not make sense
ALWAYS_INLINE T operator=(T desired) volatile noexcept
{