util: make offsetof/parent-of-member actually constexpr

This commit is contained in:
Michael Scire 2020-01-15 21:35:14 -08:00
parent f3fa680d5d
commit da59334c5e
6 changed files with 211 additions and 35 deletions

View file

@ -84,6 +84,8 @@ namespace ams::cfg {
.override_any_app = true,
};
bool g_loaded_override_config = false;
char g_hbl_sd_path[0x100] = "/atmosphere/hbl.nsp";
/* Helpers. */

View file

@ -34,6 +34,7 @@
#define ALIGNED(algn) __attribute__((aligned(algn)))
#define NORETURN __attribute__((noreturn))
#define WEAK_SYMBOL __attribute__((weak))
#define ALWAYS_INLINE_LAMBDA __attribute__((always_inline))
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define NOINLINE __attribute__((noinline))

View file

@ -557,11 +557,11 @@ namespace ams::util {
}
static constexpr Derived &GetParent(IntrusiveListNode &node) {
return static_cast<Derived &>(util::GetParentReference<Member>(&node));
return util::GetParentReference<Member, Derived>(&node);
}
static constexpr Derived const &GetParent(IntrusiveListNode const &node) {
return static_cast<const Derived &>(util::GetParentReference<Member>(&node));
return util::GetParentReference<Member, Derived>(&node);
}
};

View file

@ -257,11 +257,11 @@ namespace ams::util {
}
static constexpr Derived *GetParent(IntrusiveRedBlackTreeNode *node) {
return static_cast<Derived *>(util::GetParentPointer<Member>(node));
return util::GetParentPointer<Member, Derived>(node);
}
static constexpr Derived const *GetParent(IntrusiveRedBlackTreeNode const *node) {
return static_cast<const Derived *>(util::GetParentPointer<Member>(node));
return util::GetParentPointer<Member, Derived>(node);
}
};

View file

@ -21,21 +21,80 @@ namespace ams::util {
namespace impl {
template<typename Parent, typename Member>
union OffsetOfImpl {
Member Parent::* ptr;
intptr_t offset;
template<size_t Alignment>
struct OffsetOfUnionHolder {
template<typename ParentType, typename MemberType, size_t Offset>
union UnionImpl {
static constexpr size_t GetOffset() { return Offset; }
struct {
char padding[Offset];
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
} data;
UnionImpl<ParentType, MemberType, Offset + 1> next_union;
};
template<typename ParentType, typename MemberType>
union UnionImpl<ParentType, MemberType, 0> {
static constexpr size_t GetOffset() { return 0; }
struct {
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
} data;
UnionImpl<ParentType, MemberType, 1> next_union;
};
template<typename ParentType, typename MemberType>
union UnionImpl<ParentType, MemberType, Alignment> { /* Empty */ };
};
template<typename Parent, typename Member>
constexpr inline Parent *GetParentOfMemberImpl(Member *member, Member Parent::* ptr) {
return reinterpret_cast<Parent *>(reinterpret_cast<uintptr_t>(member) - OffsetOfImpl<Parent, Member>{ ptr }.offset);
}
template<typename ParentType, typename MemberType>
struct OffsetOfCalculator {
using UnionHolder = typename OffsetOfUnionHolder<alignof(MemberType)>::template UnionImpl<ParentType, MemberType, 0>;
union Union {
char c;
UnionHolder first_union;
ParentType parent;
template<typename Parent, typename Member>
constexpr inline Parent const *GetParentOfMemberImpl(Member const *member, Member Parent::* ptr) {
return reinterpret_cast<Parent *>(reinterpret_cast<uintptr_t>(member) - OffsetOfImpl<Parent, Member>{ ptr }.offset);
}
/* This coerces the active member to be c. */
constexpr Union() : c() { /* ... */ }
};
static constexpr Union U = {};
static constexpr const MemberType *GetNextAddress(const MemberType *start, const MemberType *target) {
while (start < target) {
start++;
}
return start;
}
static constexpr std::ptrdiff_t GetDifference(const MemberType *start, const MemberType *target) {
return (target - start) * sizeof(MemberType);
}
template<typename CurUnion>
static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member, CurUnion &cur_union) {
constexpr size_t Offset = CurUnion::GetOffset();
const auto target = std::addressof(U.parent.*member);
const auto start = std::addressof(cur_union.data.members[0]);
const auto next = GetNextAddress(start, target);
if (next < target) {
if constexpr (Offset + 1 < alignof(MemberType)) {
return OffsetOfImpl(member, cur_union.next_union);
} else {
static_assert(Offset + 1 <= alignof(MemberType));
}
}
return (next - start) * sizeof(MemberType) + Offset;
}
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
return OffsetOfImpl(member, U.first_union);
}
};
template<typename T>
struct GetMemberPointerTraits;
@ -52,26 +111,140 @@ namespace ams::util {
template<auto MemberPtr>
using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
template<auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
constexpr inline std::ptrdiff_t OffsetOf = [] {
using DeducedParentType = GetParentType<MemberPtr>;
using MemberType = GetMemberType<MemberPtr>;
static_assert(std::is_base_of<DeducedParentType, RealParentType>::value || std::is_same<RealParentType, DeducedParentType>::value);
return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
}();
}
template<auto MemberPtr>
constexpr inline impl::GetParentType<MemberPtr> *GetParentPointer(impl::GetMemberType<MemberPtr> *member) {
return impl::GetParentOfMemberImpl<impl::GetParentType<MemberPtr>, impl::GetMemberType<MemberPtr>>(member, MemberPtr);
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType<MemberPtr> *member) {
constexpr std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
uint8_t *addr = static_cast<uint8_t *>(static_cast<void *>(member));
addr -= Offset;
return *static_cast<RealParentType *>(static_cast<void *>(addr));
}
template<auto MemberPtr>
constexpr inline impl::GetParentType<MemberPtr> const *GetParentPointer(impl::GetMemberType<MemberPtr> const *member) {
return impl::GetParentOfMemberImpl<impl::GetParentType<MemberPtr>, impl::GetMemberType<MemberPtr>>(member, MemberPtr);
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType<MemberPtr> const *member) {
constexpr std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
const uint8_t *addr = static_cast<const uint8_t *>(static_cast<const void *>(member));
addr -= Offset;
return *static_cast<const RealParentType *>(static_cast<const void *>(addr));
}
template<auto MemberPtr>
constexpr inline impl::GetParentType<MemberPtr> &GetParentReference(impl::GetMemberType<MemberPtr> *member) {
return *impl::GetParentOfMemberImpl<impl::GetParentType<MemberPtr>, impl::GetMemberType<MemberPtr>>(member, MemberPtr);
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType<MemberPtr> *member) {
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
}
template<auto MemberPtr>
constexpr inline impl::GetParentType<MemberPtr> const &GetParentReference(impl::GetMemberType<MemberPtr> const *member) {
return *impl::GetParentOfMemberImpl<impl::GetParentType<MemberPtr>, impl::GetMemberType<MemberPtr>>(member, MemberPtr);
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType<MemberPtr> const *member) {
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
}
namespace test {
struct Struct1 {
uint32_t a;
};
struct Struct2 {
uint32_t b;
};
struct Struct3 : public Struct1, Struct2 {
uint32_t c;
};
static_assert(impl::OffsetOf<&Struct1::a> == 0);
static_assert(impl::OffsetOf<&Struct2::b> == 0);
static_assert(impl::OffsetOf<&Struct3::a> == 0);
static_assert(impl::OffsetOf<&Struct3::b> == 0);
static_assert(impl::OffsetOf<&Struct3::a, Struct3> == 0 || impl::OffsetOf<&Struct3::b, Struct3> == 0);
static_assert(impl::OffsetOf<&Struct3::a, Struct3> == sizeof(Struct2) || impl::OffsetOf<&Struct3::b, Struct3> == sizeof(Struct1));
static_assert(impl::OffsetOf<&Struct3::c> == sizeof(Struct1) + sizeof(Struct2));
constexpr Struct3 TestStruct3 = {};
static_assert(std::addressof(TestStruct3) == GetParentPointer<&Struct3::a, Struct3>(std::addressof(TestStruct3.a)));
static_assert(std::addressof(TestStruct3) == GetParentPointer<&Struct3::b, Struct3>(std::addressof(TestStruct3.b)));
static_assert(std::addressof(TestStruct3) == GetParentPointer<&Struct3::c, Struct3>(std::addressof(TestStruct3.c)));
struct CharArray {
char c0;
char c1;
char c2;
char c3;
char c4;
char c5;
char c6;
char c7;
};
static_assert(impl::OffsetOf<&CharArray::c0> == 0);
static_assert(impl::OffsetOf<&CharArray::c1> == 1);
static_assert(impl::OffsetOf<&CharArray::c2> == 2);
static_assert(impl::OffsetOf<&CharArray::c3> == 3);
static_assert(impl::OffsetOf<&CharArray::c4> == 4);
static_assert(impl::OffsetOf<&CharArray::c5> == 5);
static_assert(impl::OffsetOf<&CharArray::c6> == 6);
static_assert(impl::OffsetOf<&CharArray::c7> == 7);
constexpr CharArray TestCharArray = {};
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c0>(std::addressof(TestCharArray.c0)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c1>(std::addressof(TestCharArray.c1)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c2>(std::addressof(TestCharArray.c2)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c3>(std::addressof(TestCharArray.c3)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c4>(std::addressof(TestCharArray.c4)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c5>(std::addressof(TestCharArray.c5)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c6>(std::addressof(TestCharArray.c6)));
static_assert(std::addressof(TestCharArray) == GetParentPointer<&CharArray::c7>(std::addressof(TestCharArray.c7)));
}
}
/* Defines, for use by other code. */
#define OFFSETOF(parent, member) (::ams::util::OffsetOf<&parent::member, parent>)
#define AMS_GET_PARENT_TYPE_IMPL(parent, argt) typename std::conditional<std::is_pointer<argt>::value, \
typename std::conditional<std::is_const<typename std::remove_pointer<argt>::type>::value, \
const parent, \
parent \
>::type, \
typename std::conditional<std::is_const<typename std::remove_reference<argt>::type>::value, \
const parent, \
parent \
>::type \
>::type
#define GET_PARENT_PTR(parent, member, _arg) ([]<typename GetParentPtrArgType>(GetParentPtrArgType &arg) ALWAYS_INLINE_LAMBDA -> AMS_GET_PARENT_TYPE_IMPL(parent, GetParentPtrArgType) * { \
using ParentPtrType = typename std::conditional<std::is_const<GetParentPtrArgType>::value, const parent, parent>::type *; \
if constexpr (std::is_pointer<GetParentPtrArgType>::value) { \
static_assert(std::is_same<typename std::remove_cv<decltype(parent::member)>::type, typename std::remove_cv<typename std::remove_pointer<GetParentPtrArgType>::type>::type>::value); \
return ::ams::util::GetParentPointer<&parent::member, parent>(arg); \
} else { \
static_assert(std::is_same<typename std::remove_cv<decltype(parent::member)>::type, typename std::remove_cv<typename std::remove_reference<GetParentPtrArgType>::type>::type>::value); \
return ::ams::util::GetParentPointer<&parent::member, parent>(std::addressof(arg)); \
} \
}(_arg))
#define GET_PARENT_REF(parent, member, _arg) ([]<typename GetParentPtrArgType>(GetParentPtrArgType &arg) ALWAYS_INLINE_LAMBDA -> AMS_GET_PARENT_TYPE_IMPL(parent, GetParentPtrArgType) & { \
if constexpr (std::is_pointer<GetParentPtrArgType>::value) { \
static_assert(std::is_same<typename std::remove_cv<decltype(parent::member)>::type, typename std::remove_cv<typename std::remove_pointer<GetParentPtrArgType>::type>::type>::value); \
return ::ams::util::GetParentReference<&parent::member, parent>(arg); \
} else { \
static_assert(std::is_same<typename std::remove_cv<decltype(parent::member)>::type, typename std::remove_cv<typename std::remove_reference<GetParentPtrArgType>::type>::type>::value); \
return ::ams::util::GetParentReference<&parent::member, parent>(std::addressof(arg)); \
} \
}(_arg))

View file

@ -29,24 +29,24 @@ namespace ams::util {
F f;
bool active;
public:
constexpr ScopeGuard(F f) : f(std::move(f)), active(true) { }
~ScopeGuard() { if (active) { f(); } }
void Cancel() { active = false; }
constexpr ALWAYS_INLINE ScopeGuard(F f) : f(std::move(f)), active(true) { }
ALWAYS_INLINE ~ScopeGuard() { if (active) { f(); } }
ALWAYS_INLINE void Cancel() { active = false; }
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
ALWAYS_INLINE ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
rhs.Cancel();
}
};
template<class F>
constexpr ScopeGuard<F> MakeScopeGuard(F f) {
constexpr ALWAYS_INLINE ScopeGuard<F> MakeScopeGuard(F f) {
return ScopeGuard<F>(std::move(f));
}
enum class ScopeGuardOnExit {};
template <typename F>
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
constexpr ALWAYS_INLINE ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
return ScopeGuard<F>(std::forward<F>(f));
}