From d00cec38b0e9c579a652587f787ed94504ea239a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 27 Sep 2021 11:37:21 -0700 Subject: [PATCH] kern: use util::BitFlagSet for capability flags --- .../mesosphere/kern_k_capabilities.hpp | 107 +++++++----------- .../include/mesosphere/kern_k_thread.hpp | 5 +- .../mesosphere/svc/kern_svc_prototypes.hpp | 3 + .../include/vapours/util/util_bitflagset.hpp | 97 ++++++++-------- 4 files changed, 94 insertions(+), 118 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index 1d7e0a7db..ef11f3cb7 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -23,8 +23,10 @@ namespace ams::kern { class KCapabilities { private: - static constexpr size_t SvcFlagCount = svc::NumSupervisorCalls / BITSIZEOF(u8); - static constexpr size_t IrqFlagCount = /* TODO */0x80; + static constexpr size_t InterruptIdCount = 0x400; + + struct InterruptFlagSetTag{}; + using InterruptFlagSet = util::BitFlagSet; enum class CapabilityType : u32 { CorePriority = (1u << 3) - 1, @@ -154,6 +156,7 @@ namespace ams::kern { }; static const u32 PaddingInterruptId = 0x3FF; + static_assert(PaddingInterruptId < InterruptIdCount); struct InterruptPair { using IdBits = Field<0, CapabilityId + 1>; @@ -200,8 +203,8 @@ namespace ams::kern { CapabilityFlag | CapabilityFlag; private: - u8 m_svc_access_flags[SvcFlagCount]{}; - u8 m_irq_access_flags[IrqFlagCount]{}; + svc::SvcAccessFlagSet m_svc_access_flags{}; + InterruptFlagSet m_irq_access_flags{}; u64 m_core_mask{}; u64 m_priority_mask{}; util::BitPack32 m_debug_capabilities{0}; @@ -209,37 +212,19 @@ namespace ams::kern { util::BitPack32 m_intended_kernel_version{0}; u32 m_program_type{}; private: - static constexpr ALWAYS_INLINE void SetSvcAllowedImpl(u8 *data, u32 id) { - constexpr size_t BitsPerWord = BITSIZEOF(*data); - MESOSPHERE_ASSERT(id < svc::SvcId_Count); - data[id / BitsPerWord] |= (1ul << (id % BitsPerWord)); - } - - static constexpr ALWAYS_INLINE void ClearSvcAllowedImpl(u8 *data, u32 id) { - constexpr size_t BitsPerWord = BITSIZEOF(*data); - MESOSPHERE_ASSERT(id < svc::SvcId_Count); - data[id / BitsPerWord] &= ~(1ul << (id % BitsPerWord)); - } - - static constexpr ALWAYS_INLINE bool GetSvcAllowedImpl(const u8 *data, u32 id) { - constexpr size_t BitsPerWord = BITSIZEOF(*data); - MESOSPHERE_ASSERT(id < svc::SvcId_Count); - return (data[id / BitsPerWord] & (1ul << (id % BitsPerWord))) != 0; - } - - bool SetSvcAllowed(u32 id) { - if (id < BITSIZEOF(m_svc_access_flags)) { - SetSvcAllowedImpl(m_svc_access_flags, id); + constexpr bool SetSvcAllowed(u32 id) { + if (AMS_LIKELY(id < m_svc_access_flags.GetCount())) { + m_svc_access_flags[id] = true; return true; } else { return false; } } - bool SetInterruptPermitted(u32 id) { + constexpr bool SetInterruptPermitted(u32 id) { constexpr size_t BitsPerWord = BITSIZEOF(m_irq_access_flags[0]); - if (id < BITSIZEOF(m_irq_access_flags)) { - m_irq_access_flags[id / BitsPerWord] |= (1ul << (id % BitsPerWord)); + if (AMS_LIKELY(id < m_irq_access_flags.GetCount())) { + m_irq_access_flags[id] = true; return true; } else { return false; @@ -271,94 +256,80 @@ namespace ams::kern { constexpr s32 GetHandleTableSize() const { return m_handle_table_size; } ALWAYS_INLINE void CopySvcPermissionsTo(KThread::StackParameters &sp) const { - static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission)); /* Copy permissions. */ - std::memcpy(sp.svc_permission, m_svc_access_flags, sizeof(m_svc_access_flags)); + sp.svc_access_flags = m_svc_access_flags; /* Clear specific SVCs based on our state. */ - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState); + sp.svc_access_flags[svc::SvcId_ReturnFromException] = false; + sp.svc_access_flags[svc::SvcId_SynchronizePreemptionState] = false; if (sp.is_pinned) { - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); + sp.svc_access_flags[svc::SvcId_GetInfo] = false; } } ALWAYS_INLINE void CopyPinnedSvcPermissionsTo(KThread::StackParameters &sp) const { - static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission)); /* Get whether we have access to return from exception. */ - const bool return_from_exception = GetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + const bool return_from_exception = sp.svc_access_flags[svc::SvcId_ReturnFromException]; /* Clear all permissions. */ - std::memset(sp.svc_permission, 0, sizeof(m_svc_access_flags)); + sp.svc_access_flags.Reset(); /* Set SynchronizePreemptionState if allowed. */ - if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_SynchronizePreemptionState)) { - SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState); + if (m_svc_access_flags[svc::SvcId_SynchronizePreemptionState]) { + sp.svc_access_flags[svc::SvcId_SynchronizePreemptionState] = true; } /* If we previously had ReturnFromException, potentially grant it and GetInfo. */ if (return_from_exception) { - /* Set ReturnFromException if allowed. */ - if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_ReturnFromException)) { - SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); - } + /* Set ReturnFromException (guaranteed allowed, if we're here). */ + sp.svc_access_flags[svc::SvcId_ReturnFromException] = true; /* Set GetInfo if allowed. */ - if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_GetInfo)) { - SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); + if (m_svc_access_flags[svc::SvcId_GetInfo]) { + sp.svc_access_flags[svc::SvcId_GetInfo] = true; } } } ALWAYS_INLINE void CopyUnpinnedSvcPermissionsTo(KThread::StackParameters &sp) const { - static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission)); /* Get whether we have access to return from exception. */ - const bool return_from_exception = GetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + const bool return_from_exception = sp.svc_access_flags[svc::SvcId_ReturnFromException]; /* Copy permissions. */ - std::memcpy(sp.svc_permission, m_svc_access_flags, sizeof(m_svc_access_flags)); + sp.svc_access_flags = m_svc_access_flags; - /* Clear/Set specific SVCs based on our state. */ - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState); - if (return_from_exception) { - SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + /* Clear specific SVCs based on our state. */ + sp.svc_access_flags[svc::SvcId_SynchronizePreemptionState] = false; + + if (!return_from_exception) { + sp.svc_access_flags[svc::SvcId_ReturnFromException] = false; } } ALWAYS_INLINE void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) const { - static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission)); - /* Set ReturnFromException if allowed. */ - if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_ReturnFromException)) { - SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + if (m_svc_access_flags[svc::SvcId_ReturnFromException]) { + sp.svc_access_flags[svc::SvcId_ReturnFromException] = true; } /* Set GetInfo if allowed. */ - if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_GetInfo)) { - SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); + if (m_svc_access_flags[svc::SvcId_GetInfo]) { + sp.svc_access_flags[svc::SvcId_GetInfo] = true; } } ALWAYS_INLINE void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) const { - static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission)); - /* Clear ReturnFromException. */ - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); + sp.svc_access_flags[svc::SvcId_ReturnFromException] = false; /* If pinned, clear GetInfo. */ if (sp.is_pinned) { - ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); + sp.svc_access_flags[svc::SvcId_GetInfo] = false; } } constexpr bool IsPermittedInterrupt(u32 id) const { - constexpr size_t BitsPerWord = BITSIZEOF(m_irq_access_flags[0]); - if (id < BITSIZEOF(m_irq_access_flags)) { - return (m_irq_access_flags[id / BitsPerWord] & (1ul << (id % BitsPerWord))) != 0; - } else { - return false; - } + return (id < m_irq_access_flags.GetCount()) && m_irq_access_flags[id]; } constexpr bool IsPermittedDebug() const { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 98ac53f63..97a6a7bf8 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #pragma once +#include #include #include #include @@ -84,7 +85,7 @@ namespace ams::kern { }; struct StackParameters { - alignas(0x10) u8 svc_permission[0x18]; + alignas(0x10) svc::SvcAccessFlagSet svc_access_flags; KThreadContext *context; KThread *cur_thread; s16 disable_count; @@ -100,7 +101,7 @@ namespace ams::kern { static_assert(alignof(StackParameters) == 0x10); static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE); - static_assert(__builtin_offsetof(StackParameters, svc_permission) == THREAD_STACK_PARAMETERS_SVC_PERMISSION); + static_assert(__builtin_offsetof(StackParameters, svc_access_flags) == THREAD_STACK_PARAMETERS_SVC_PERMISSION); static_assert(__builtin_offsetof(StackParameters, context) == THREAD_STACK_PARAMETERS_CONTEXT); static_assert(__builtin_offsetof(StackParameters, cur_thread) == THREAD_STACK_PARAMETERS_CUR_THREAD); static_assert(__builtin_offsetof(StackParameters, disable_count) == THREAD_STACK_PARAMETERS_DISABLE_COUNT); diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp index a9163b56e..f73313997 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp @@ -45,5 +45,8 @@ namespace ams::kern::svc { #undef AMS_KERN_SVC_DECLARE_PROTOTYPE_64 #undef AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32 + struct SvcAccessFlagSetTag{}; + + using SvcAccessFlagSet = util::BitFlagSet; } diff --git a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp index 6c3447193..b0d8935b3 100644 --- a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp @@ -25,35 +25,35 @@ namespace ams::util { namespace impl { template - constexpr void NegateImpl(Storage arr[]) { + constexpr ALWAYS_INLINE void NegateImpl(Storage arr[]) { for (size_t i = 0; i < Count; i++) { arr[i] = ~arr[i]; } } template - constexpr void AndImpl(Storage dst[], const Storage src[]) { + constexpr ALWAYS_INLINE void AndImpl(Storage dst[], const Storage src[]) { for (size_t i = 0; i < Count; i++) { dst[i] &= src[i]; } } template - constexpr void OrImpl(Storage dst[], const Storage src[]) { + constexpr ALWAYS_INLINE void OrImpl(Storage dst[], const Storage src[]) { for (size_t i = 0; i < Count; i++) { dst[i] |= src[i]; } } template - constexpr void XorImpl(Storage dst[], const Storage src[]) { + constexpr ALWAYS_INLINE void XorImpl(Storage dst[], const Storage src[]) { for (size_t i = 0; i < Count; i++) { dst[i] ^= src[i]; } } template - constexpr bool IsEqual(const Storage lhs[], const Storage rhs[]) { + constexpr ALWAYS_INLINE bool IsEqual(const Storage lhs[], const Storage rhs[]) { for (size_t i = 0; i < Count; i++) { if (lhs[i] != rhs[i]) { return false; @@ -63,7 +63,7 @@ namespace ams::util { } template - constexpr bool IsAnySet(const Storage arr[]) { + constexpr ALWAYS_INLINE bool IsAnySet(const Storage arr[]) { for (size_t i = 0; i < Count; i++) { if (arr[i]) { return true; @@ -73,7 +73,7 @@ namespace ams::util { } template - constexpr int PopCount(const Storage arr[]) { + constexpr ALWAYS_INLINE int PopCount(const Storage arr[]) { int count = 0; for (size_t i = 0; i < Count; i++) { count += ::ams::util::PopCount(arr[i]); @@ -91,7 +91,7 @@ namespace ams::util { static constexpr size_t StorageCount = util::AlignUp(N, StorageBitCount) / StorageBitCount; Storage _storage[StorageCount]; private: - constexpr BitFlagSet &SetImpl(s32 idx, Storage mask, bool en) { + constexpr ALWAYS_INLINE BitFlagSet &SetImpl(s32 idx, Storage mask, bool en) { if (en) { this->_storage[idx] |= mask; } else { @@ -100,13 +100,13 @@ namespace ams::util { return *this; } - constexpr bool TestImpl(s32 idx, Storage mask) const { return (this->_storage[idx] & mask) != 0; } - constexpr void Truncate() { TruncateIf(std::integral_constant{}); } - constexpr void TruncateIf(std::true_type) { this->_storage[StorageCount - 1] &= MakeStorageMask(N) - 1; } - constexpr void TruncateIf(std::false_type) { /* ... */ } + constexpr ALWAYS_INLINE bool TestImpl(s32 idx, Storage mask) const { return (this->_storage[idx] & mask) != 0; } + constexpr ALWAYS_INLINE void Truncate() { TruncateIf(std::integral_constant{}); } + constexpr ALWAYS_INLINE void TruncateIf(std::true_type) { this->_storage[StorageCount - 1] &= MakeStorageMask(N) - 1; } + constexpr ALWAYS_INLINE void TruncateIf(std::false_type) { /* ... */ } - static constexpr s32 GetStorageIndex(s32 idx) { return idx / StorageBitCount; } - static constexpr Storage MakeStorageMask(s32 idx) { return static_cast(1) << (idx % StorageBitCount); } + static constexpr ALWAYS_INLINE s32 GetStorageIndex(s32 idx) { return idx / StorageBitCount; } + static constexpr ALWAYS_INLINE Storage MakeStorageMask(s32 idx) { return static_cast(1) << (idx % StorageBitCount); } public: class Reference { friend struct BitFlagSet; @@ -114,14 +114,14 @@ namespace ams::util { BitFlagSet *set; s32 idx; private: - constexpr Reference() : set(nullptr), idx(0) { /* ... */ } - constexpr Reference(BitFlagSet &s, s32 i) : set(std::addressof(s)), idx(i) { /* ... */ } + constexpr ALWAYS_INLINE Reference() : set(nullptr), idx(0) { /* ... */ } + constexpr ALWAYS_INLINE Reference(BitFlagSet &s, s32 i) : set(std::addressof(s)), idx(i) { /* ... */ } public: - constexpr Reference &operator=(bool en) { this->set->Set(this->idx, en); return *this; } - constexpr Reference &operator=(const Reference &r) { this->set->Set(this->idx, r); return *this; } - constexpr Reference &Negate() { this->set->Negate(this->idx); return *this; } - constexpr operator bool() const { return this->set->Test(this->idx); } - constexpr bool operator~() const { return !this->set->Test(this->idx); } + constexpr ALWAYS_INLINE Reference &operator=(bool en) { this->set->Set(this->idx, en); return *this; } + constexpr ALWAYS_INLINE Reference &operator=(const Reference &r) { this->set->Set(this->idx, r); return *this; } + constexpr ALWAYS_INLINE Reference &Negate() { this->set->Negate(this->idx); return *this; } + constexpr ALWAYS_INLINE operator bool() const { return this->set->Test(this->idx); } + constexpr ALWAYS_INLINE bool operator~() const { return !this->set->Test(this->idx); } }; template @@ -142,45 +142,46 @@ namespace ams::util { }; template - constexpr bool Test() const { return this->TestImpl(FlagType::StorageIndex, FlagType::StorageMask); } - constexpr bool Test(s32 idx) const { return this->TestImpl(GetStorageIndex(idx), MakeStorageMask(idx)); } + constexpr ALWAYS_INLINE bool Test() const { return this->TestImpl(FlagType::StorageIndex, FlagType::StorageMask); } + constexpr ALWAYS_INLINE bool Test(s32 idx) const { return this->TestImpl(GetStorageIndex(idx), MakeStorageMask(idx)); } template - constexpr BitFlagSet &Set(bool en = true) { return this->SetImpl(FlagType::StorageIndex, FlagType::StorageMask, en); } - constexpr BitFlagSet &Set(s32 idx, bool en = true) { return this->SetImpl(GetStorageIndex(idx), MakeStorageMask(idx), en); } - constexpr BitFlagSet &Set() { std::memset(this->_storage, ~0, sizeof(this->_storage)); this->Truncate(); return *this; } + constexpr ALWAYS_INLINE BitFlagSet &Set(bool en = true) { return this->SetImpl(FlagType::StorageIndex, FlagType::StorageMask, en); } + constexpr ALWAYS_INLINE BitFlagSet &Set(s32 idx, bool en = true) { return this->SetImpl(GetStorageIndex(idx), MakeStorageMask(idx), en); } + constexpr ALWAYS_INLINE BitFlagSet &Set() { std::memset(this->_storage, ~0, sizeof(this->_storage)); this->Truncate(); return *this; } template - constexpr BitFlagSet &Reset() { return this->Set(false); } - constexpr BitFlagSet &Reset(s32 idx) { return this->Set(idx, false); } - constexpr BitFlagSet &Reset() { std::memset(this->_storage, 0, sizeof(this->_storage)); this->Truncate(); return *this; } + constexpr ALWAYS_INLINE BitFlagSet &Reset() { return this->Set(false); } + constexpr ALWAYS_INLINE BitFlagSet &Reset(s32 idx) { return this->Set(idx, false); } + constexpr ALWAYS_INLINE BitFlagSet &Reset() { std::memset(this->_storage, 0, sizeof(this->_storage)); this->Truncate(); return *this; } template - constexpr BitFlagSet &Negate() { return this->Set(!this->Test()); } - constexpr BitFlagSet &Negate(s32 idx) { return this->Set(idx, !this->Test(idx)); } - constexpr BitFlagSet &Negate() { ams::util::impl::NegateImpl(this->_storage); this->Truncate(); return *this; } + constexpr ALWAYS_INLINE BitFlagSet &Negate() { return this->Set(!this->Test()); } + constexpr ALWAYS_INLINE BitFlagSet &Negate(s32 idx) { return this->Set(idx, !this->Test(idx)); } + constexpr ALWAYS_INLINE BitFlagSet &Negate() { ams::util::impl::NegateImpl(this->_storage); this->Truncate(); return *this; } - constexpr int GetCount() const { return static_cast(N); } - constexpr bool IsAnySet() const { return ams::util::impl::IsAnySet(this->_storage); } - constexpr int PopCount() const { return ams::util::impl::PopCount(this->_storage); } - constexpr bool IsAllSet() const { return this->PopCount() == this->GetCount(); } - constexpr bool IsAllOff() const { return !this->IsAnySet(); } + consteval static int GetCount() { return static_cast(N); } - constexpr bool operator[](s32 idx) const { return this->Test(idx); } - constexpr Reference operator[](s32 idx) { return Reference(*this, idx); } + constexpr ALWAYS_INLINE bool IsAnySet() const { return ams::util::impl::IsAnySet(this->_storage); } + constexpr ALWAYS_INLINE int PopCount() const { return ams::util::impl::PopCount(this->_storage); } + constexpr ALWAYS_INLINE bool IsAllSet() const { return this->PopCount() == this->GetCount(); } + constexpr ALWAYS_INLINE bool IsAllOff() const { return !this->IsAnySet(); } - constexpr bool operator==(const BitFlagSet &rhs) const { return ams::util::impl::IsEqual(this->_storage, rhs._storage); } - constexpr bool operator!=(const BitFlagSet &rhs) const { return !(*this == rhs); } + constexpr ALWAYS_INLINE bool operator[](s32 idx) const { return this->Test(idx); } + constexpr ALWAYS_INLINE Reference operator[](s32 idx) { return Reference(*this, idx); } - constexpr BitFlagSet operator~() const { BitFlagSet tmp = *this; return tmp.Negate(); } + constexpr ALWAYS_INLINE bool operator==(const BitFlagSet &rhs) const { return ams::util::impl::IsEqual(this->_storage, rhs._storage); } + constexpr ALWAYS_INLINE bool operator!=(const BitFlagSet &rhs) const { return !(*this == rhs); } - constexpr BitFlagSet operator&(const BitFlagSet &rhs) const { BitFlagSet v = *this; v &= rhs; return v; } - constexpr BitFlagSet operator^(const BitFlagSet &rhs) const { BitFlagSet v = *this; v ^= rhs; return v; } - constexpr BitFlagSet operator|(const BitFlagSet &rhs) const { BitFlagSet v = *this; v |= rhs; return v; } + constexpr ALWAYS_INLINE BitFlagSet operator~() const { BitFlagSet tmp = *this; return tmp.Negate(); } - constexpr BitFlagSet &operator&=(const BitFlagSet &rhs) { ams::util::impl::AndImpl(this->_storage, rhs._storage); return *this; } - constexpr BitFlagSet &operator^=(const BitFlagSet &rhs) { ams::util::impl::XorImpl(this->_storage, rhs._storage); return *this; } - constexpr BitFlagSet &operator|=(const BitFlagSet &rhs) { ams::util::impl::OrImpl(this->_storage, rhs._storage); return *this; } + constexpr ALWAYS_INLINE BitFlagSet operator&(const BitFlagSet &rhs) const { BitFlagSet v = *this; v &= rhs; return v; } + constexpr ALWAYS_INLINE BitFlagSet operator^(const BitFlagSet &rhs) const { BitFlagSet v = *this; v ^= rhs; return v; } + constexpr ALWAYS_INLINE BitFlagSet operator|(const BitFlagSet &rhs) const { BitFlagSet v = *this; v |= rhs; return v; } + + constexpr ALWAYS_INLINE BitFlagSet &operator&=(const BitFlagSet &rhs) { ams::util::impl::AndImpl(this->_storage, rhs._storage); return *this; } + constexpr ALWAYS_INLINE BitFlagSet &operator^=(const BitFlagSet &rhs) { ams::util::impl::XorImpl(this->_storage, rhs._storage); return *this; } + constexpr ALWAYS_INLINE BitFlagSet &operator|=(const BitFlagSet &rhs) { ams::util::impl::OrImpl(this->_storage, rhs._storage); return *this; } }; template