diff options
Diffstat (limited to 'clang-r353983/include/llvm/ADT/PointerSumType.h')
| -rw-r--r-- | clang-r353983/include/llvm/ADT/PointerSumType.h | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/clang-r353983/include/llvm/ADT/PointerSumType.h b/clang-r353983/include/llvm/ADT/PointerSumType.h new file mode 100644 index 00000000..d467f83f --- /dev/null +++ b/clang-r353983/include/llvm/ADT/PointerSumType.h @@ -0,0 +1,294 @@ +//===- llvm/ADT/PointerSumType.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_POINTERSUMTYPE_H +#define LLVM_ADT_POINTERSUMTYPE_H + +#include "llvm/ADT/bit.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/PointerLikeTypeTraits.h" +#include <cassert> +#include <cstdint> +#include <type_traits> + +namespace llvm { + +/// A compile time pair of an integer tag and the pointer-like type which it +/// indexes within a sum type. Also allows the user to specify a particular +/// traits class for pointer types with custom behavior such as over-aligned +/// allocation. +template <uintptr_t N, typename PointerArgT, + typename TraitsArgT = PointerLikeTypeTraits<PointerArgT>> +struct PointerSumTypeMember { + enum { Tag = N }; + using PointerT = PointerArgT; + using TraitsT = TraitsArgT; +}; + +namespace detail { + +template <typename TagT, typename... MemberTs> struct PointerSumTypeHelper; + +} // end namespace detail + +/// A sum type over pointer-like types. +/// +/// This is a normal tagged union across pointer-like types that uses the low +/// bits of the pointers to store the tag. +/// +/// Each member of the sum type is specified by passing a \c +/// PointerSumTypeMember specialization in the variadic member argument list. +/// This allows the user to control the particular tag value associated with +/// a particular type, use the same type for multiple different tags, and +/// customize the pointer-like traits used for a particular member. Note that +/// these *must* be specializations of \c PointerSumTypeMember, no other type +/// will suffice, even if it provides a compatible interface. +/// +/// This type implements all of the comparison operators and even hash table +/// support by comparing the underlying storage of the pointer values. It +/// doesn't support delegating to particular members for comparisons. +/// +/// It also default constructs to a zero tag with a null pointer, whatever that +/// would be. This means that the zero value for the tag type is significant +/// and may be desirable to set to a state that is particularly desirable to +/// default construct. +/// +/// Having a supported zero-valued tag also enables getting the address of a +/// pointer stored with that tag provided it is stored in its natural bit +/// representation. This works because in the case of a zero-valued tag, the +/// pointer's value is directly stored into this object and we can expose the +/// address of that internal storage. This is especially useful when building an +/// `ArrayRef` of a single pointer stored in a sum type. +/// +/// There is no support for constructing or accessing with a dynamic tag as +/// that would fundamentally violate the type safety provided by the sum type. +template <typename TagT, typename... MemberTs> class PointerSumType { + using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>; + + // We keep both the raw value and the min tag value's pointer in a union. When + // the minimum tag value is zero, this allows code below to cleanly expose the + // address of the zero-tag pointer instead of just the zero-tag pointer + // itself. This is especially useful when building `ArrayRef`s out of a single + // pointer. However, we have to carefully access the union due to the active + // member potentially changing. When we *store* a new value, we directly + // access the union to allow us to store using the obvious types. However, + // when we *read* a value, we copy the underlying storage out to avoid relying + // on one member or the other being active. + union StorageT { + // Ensure we get a null default constructed value. We don't use a member + // initializer because some compilers seem to not implement those correctly + // for a union. + StorageT() : Value(0) {} + + uintptr_t Value; + + typename HelperT::template Lookup<HelperT::MinTag>::PointerT MinTagPointer; + }; + + StorageT Storage; + +public: + constexpr PointerSumType() = default; + + /// A typed setter to a given tagged member of the sum type. + template <TagT N> + void set(typename HelperT::template Lookup<N>::PointerT Pointer) { + void *V = HelperT::template Lookup<N>::TraitsT::getAsVoidPointer(Pointer); + assert((reinterpret_cast<uintptr_t>(V) & HelperT::TagMask) == 0 && + "Pointer is insufficiently aligned to store the discriminant!"); + Storage.Value = reinterpret_cast<uintptr_t>(V) | N; + } + + /// A typed constructor for a specific tagged member of the sum type. + template <TagT N> + static PointerSumType + create(typename HelperT::template Lookup<N>::PointerT Pointer) { + PointerSumType Result; + Result.set<N>(Pointer); + return Result; + } + + /// Clear the value to null with the min tag type. + void clear() { set<HelperT::MinTag>(nullptr); } + + TagT getTag() const { + return static_cast<TagT>(getOpaqueValue() & HelperT::TagMask); + } + + template <TagT N> bool is() const { return N == getTag(); } + + template <TagT N> typename HelperT::template Lookup<N>::PointerT get() const { + void *P = is<N>() ? getVoidPtr() : nullptr; + return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(P); + } + + template <TagT N> + typename HelperT::template Lookup<N>::PointerT cast() const { + assert(is<N>() && "This instance has a different active member."); + return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer( + getVoidPtr()); + } + + /// If the tag is zero and the pointer's value isn't changed when being + /// stored, get the address of the stored value type-punned to the zero-tag's + /// pointer type. + typename HelperT::template Lookup<HelperT::MinTag>::PointerT const * + getAddrOfZeroTagPointer() const { + return const_cast<PointerSumType *>(this)->getAddrOfZeroTagPointer(); + } + + /// If the tag is zero and the pointer's value isn't changed when being + /// stored, get the address of the stored value type-punned to the zero-tag's + /// pointer type. + typename HelperT::template Lookup<HelperT::MinTag>::PointerT * + getAddrOfZeroTagPointer() { + static_assert(HelperT::MinTag == 0, "Non-zero minimum tag value!"); + assert(is<HelperT::MinTag>() && "The active tag is not zero!"); + // Store the initial value of the pointer when read out of our storage. + auto InitialPtr = get<HelperT::MinTag>(); + // Now update the active member of the union to be the actual pointer-typed + // member so that accessing it indirectly through the returned address is + // valid. + Storage.MinTagPointer = InitialPtr; + // Finally, validate that this was a no-op as expected by reading it back + // out using the same underlying-storage read as above. + assert(InitialPtr == get<HelperT::MinTag>() && + "Switching to typed storage changed the pointer returned!"); + // Now we can correctly return an address to typed storage. + return &Storage.MinTagPointer; + } + + explicit operator bool() const { + return getOpaqueValue() & HelperT::PointerMask; + } + bool operator==(const PointerSumType &R) const { + return getOpaqueValue() == R.getOpaqueValue(); + } + bool operator!=(const PointerSumType &R) const { + return getOpaqueValue() != R.getOpaqueValue(); + } + bool operator<(const PointerSumType &R) const { + return getOpaqueValue() < R.getOpaqueValue(); + } + bool operator>(const PointerSumType &R) const { + return getOpaqueValue() > R.getOpaqueValue(); + } + bool operator<=(const PointerSumType &R) const { + return getOpaqueValue() <= R.getOpaqueValue(); + } + bool operator>=(const PointerSumType &R) const { + return getOpaqueValue() >= R.getOpaqueValue(); + } + + uintptr_t getOpaqueValue() const { + // Read the underlying storage of the union, regardless of the active + // member. + return bit_cast<uintptr_t>(Storage); + } + +protected: + void *getVoidPtr() const { + return reinterpret_cast<void *>(getOpaqueValue() & HelperT::PointerMask); + } +}; + +namespace detail { + +/// A helper template for implementing \c PointerSumType. It provides fast +/// compile-time lookup of the member from a particular tag value, along with +/// useful constants and compile time checking infrastructure.. +template <typename TagT, typename... MemberTs> +struct PointerSumTypeHelper : MemberTs... { + // First we use a trick to allow quickly looking up information about + // a particular member of the sum type. This works because we arranged to + // have this type derive from all of the member type templates. We can select + // the matching member for a tag using type deduction during overload + // resolution. + template <TagT N, typename PointerT, typename TraitsT> + static PointerSumTypeMember<N, PointerT, TraitsT> + LookupOverload(PointerSumTypeMember<N, PointerT, TraitsT> *); + template <TagT N> static void LookupOverload(...); + template <TagT N> struct Lookup { + // Compute a particular member type by resolving the lookup helper ovorload. + using MemberT = decltype( + LookupOverload<N>(static_cast<PointerSumTypeHelper *>(nullptr))); + + /// The Nth member's pointer type. + using PointerT = typename MemberT::PointerT; + + /// The Nth member's traits type. + using TraitsT = typename MemberT::TraitsT; + }; + + // Next we need to compute the number of bits available for the discriminant + // by taking the min of the bits available for each member. Much of this + // would be amazingly easier with good constexpr support. + template <uintptr_t V, uintptr_t... Vs> + struct Min : std::integral_constant< + uintptr_t, (V < Min<Vs...>::value ? V : Min<Vs...>::value)> { + }; + template <uintptr_t V> + struct Min<V> : std::integral_constant<uintptr_t, V> {}; + enum { NumTagBits = Min<MemberTs::TraitsT::NumLowBitsAvailable...>::value }; + + // Also compute the smallest discriminant and various masks for convenience. + constexpr static TagT MinTag = + static_cast<TagT>(Min<MemberTs::Tag...>::value); + enum : uint64_t { + PointerMask = static_cast<uint64_t>(-1) << NumTagBits, + TagMask = ~PointerMask + }; + + // Finally we need a recursive template to do static checks of each + // member. + template <typename MemberT, typename... InnerMemberTs> + struct Checker : Checker<InnerMemberTs...> { + static_assert(MemberT::Tag < (1 << NumTagBits), + "This discriminant value requires too many bits!"); + }; + template <typename MemberT> struct Checker<MemberT> : std::true_type { + static_assert(MemberT::Tag < (1 << NumTagBits), + "This discriminant value requires too many bits!"); + }; + static_assert(Checker<MemberTs...>::value, + "Each member must pass the checker."); +}; + +} // end namespace detail + +// Teach DenseMap how to use PointerSumTypes as keys. +template <typename TagT, typename... MemberTs> +struct DenseMapInfo<PointerSumType<TagT, MemberTs...>> { + using SumType = PointerSumType<TagT, MemberTs...>; + using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>; + enum { SomeTag = HelperT::MinTag }; + using SomePointerT = + typename HelperT::template Lookup<HelperT::MinTag>::PointerT; + using SomePointerInfo = DenseMapInfo<SomePointerT>; + + static inline SumType getEmptyKey() { + return SumType::create<SomeTag>(SomePointerInfo::getEmptyKey()); + } + + static inline SumType getTombstoneKey() { + return SumType::create<SomeTag>(SomePointerInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const SumType &Arg) { + uintptr_t OpaqueValue = Arg.getOpaqueValue(); + return DenseMapInfo<uintptr_t>::getHashValue(OpaqueValue); + } + + static bool isEqual(const SumType &LHS, const SumType &RHS) { + return LHS == RHS; + } +}; + +} // end namespace llvm + +#endif // LLVM_ADT_POINTERSUMTYPE_H |
