//===- llvm/ADT/PointerUnion.h - Pointer Type Union -------------*- 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 // //===----------------------------------------------------------------------===// /// /// \file /// This file defines the PointerUnion class, which is a discriminated union of /// pointer types. /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_POINTERUNION_H #define LLVM_ADT_POINTERUNION_H #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include #include #include #include #include #include namespace llvm { namespace pointer_union_detail { /// Determine the number of bits required to store values in [0, NumValues). /// This is ceil(log2(NumValues)). constexpr int bitsRequired(unsigned NumValues) { return NumValues == 0 ? 0 : llvm::bit_width_constexpr(NumValues - 1); } template constexpr int lowBitsAvailable() { return std::min( {static_cast(PointerLikeTypeTraits::NumLowBitsAvailable)...}); } /// True if all types have enough low bits for a fixed-width tag. template constexpr bool useFixedWidthTags() { return lowBitsAvailable() >= bitsRequired(sizeof...(PTs)); } /// True if types are in non-decreasing NumLowBitsAvailable order. // TODO: Switch to llvm::is_sorted when it becomes constexpr. template constexpr bool typesInNonDecreasingBitOrder() { int Bits[] = {PointerLikeTypeTraits::NumLowBitsAvailable...}; for (size_t I = 1; I < sizeof...(PTs); ++I) if (Bits[I] < Bits[I - 1]) return false; return true; } /// Tag descriptor for one type in the union. struct TagEntry { uintptr_t Value; // Bit pattern stored in the low bits. uintptr_t Mask; // Mask covering all tag bits for this entry. }; /// Compute fixed-width tag table (all types have enough bits for the tag). /// For example, with 4 types and 3 available bits, the tag is 2 bits wide /// (values 0-3) and each entry has the same mask of 0x3. template constexpr std::array computeFixedTags() { constexpr size_t N = sizeof...(PTs); constexpr uintptr_t TagMask = (uintptr_t(1) << bitsRequired(N)) - 1; std::array Result = {}; for (size_t I = 0; I < N; ++I) { Result[I].Value = uintptr_t(I); Result[I].Mask = TagMask; } return Result; } /// Compute variable-width tag table, or return std::nullopt if the types /// don't fit. Types must be in non-decreasing NumLowBitsAvailable order. /// Groups types by available bits into tiers; each non-final tier reserves /// its highest code as an escape prefix. /// /// Example with 3 tiers (2-bit, 3-bit, 5-bit types): /// Tier 0 (2 bits): codes 0b00, 0b01, 0b10; escape = 0b11 /// Tier 1 (3 bits): codes 0b011, escape = 0b111 /// Tier 2 (5 bits): codes 0b00111, 0b01111, 0b10111, 0b11111 template constexpr std::optional> computeExtendedTags() { constexpr size_t N = sizeof...(PTs); std::array Result = {}; int Bits[] = {PointerLikeTypeTraits::NumLowBitsAvailable...}; uintptr_t EscapePrefix = 0; int PrevBits = 0; size_t I = 0; // Walk tiers (groups of types with the same NumLowBitsAvailable). For each // tier, assign tag values using the new bits introduced by this tier, // prefixed by the accumulated escape codes from previous tiers. Non-final // tiers reserve their highest code as an escape to the next tier. while (I < N) { int TierBits = Bits[I]; if (TierBits < PrevBits) return std::nullopt; int NewBits = TierBits - PrevBits; size_t TierEnd = I; while (TierEnd < N && Bits[TierEnd] == TierBits) ++TierEnd; bool IsLastTier = (TierEnd == N); size_t TypesInTier = TierEnd - I; size_t Capacity = IsLastTier ? (size_t(1) << NewBits) : ((size_t(1) << NewBits) - 1); if (TypesInTier > Capacity) return std::nullopt; for (size_t J = 0; J < TypesInTier; ++J) { Result[I + J].Value = EscapePrefix | (uintptr_t(J) << PrevBits); Result[I + J].Mask = (uintptr_t(1) << TierBits) - 1; } uintptr_t EscapeCode = (uintptr_t(1) << NewBits) - 1; EscapePrefix |= EscapeCode << PrevBits; PrevBits = TierBits; I = TierEnd; } return Result; } /// CRTP base that generates non-template constructors and assignment operators /// for each type in the union. Non-template constructors allow implicit /// conversions (derived-to-base, non-const-to-const). template class PointerUnionMembers; template class PointerUnionMembers { protected: detail::PunnedPointer Val; PointerUnionMembers() : Val(uintptr_t(0)) {} template friend struct ::llvm::CastInfo; template friend struct ::llvm::PointerLikeTypeTraits; }; template class PointerUnionMembers : public PointerUnionMembers { using Base = PointerUnionMembers; public: using Base::Base; PointerUnionMembers() = default; PointerUnionMembers(Type V) { this->Val = Derived::encode(V); } using Base::operator=; Derived &operator=(Type V) { this->Val = Derived::encode(V); return static_cast(*this); } }; } // end namespace pointer_union_detail /// A discriminated union of two or more pointer types, with the discriminator /// in the low bits of the pointer. /// /// This implementation is extremely efficient in space due to leveraging the /// low bits of the pointer, while exposing a natural and type-safe API. /// /// When all types have enough alignment for a fixed-width tag, /// the tag is placed in the high end of the available low bits, leaving spare /// low bits for nesting in PointerIntPair or SmallPtrSet. When types have /// heterogeneous alignment, a variable-length escape-encoded tag /// is used; in that case, types must be listed in non-decreasing /// NumLowBitsAvailable order. /// /// Common use patterns would be something like this: /// PointerUnion P; /// P = (int*)0; /// printf("%d %d", P.is(), P.is()); // prints "1 0" /// X = P.get(); // ok. /// Y = P.get(); // runtime assertion failure. /// Z = P.get(); // compile time failure. /// P = (float*)0; /// Y = P.get(); // ok. /// X = P.get(); // runtime assertion failure. /// PointerUnion Q; // compile time failure. template class PointerUnion : public pointer_union_detail::PointerUnionMembers, 0, PTs...> { static_assert(sizeof...(PTs) > 0, "PointerUnion must have at least one type"); static_assert(TypesAreDistinct::value, "PointerUnion alternative types cannot be repeated"); using Base = typename PointerUnion::PointerUnionMembers; using First = TypeAtIndex<0, PTs...>; template friend class pointer_union_detail::PointerUnionMembers; template friend struct CastInfo; template friend struct PointerLikeTypeTraits; // These are constexpr functions rather than static constexpr data members // so that alignof() on potentially incomplete types is not evaluated at // class-definition time. static constexpr bool useFixedWidthTags() { return pointer_union_detail::useFixedWidthTags(); } static constexpr int minLowBitsAvailable() { return pointer_union_detail::lowBitsAvailable(); } static constexpr int tagBits() { return pointer_union_detail::bitsRequired(sizeof...(PTs)); } /// When using fixed-width tags, the tag is shifted to the high end of the /// available low bits so that the lowest bits remain free for nesting. With /// variable-width encoding mode, the tag starts at bit 0. static constexpr int tagShift() { return useFixedWidthTags() ? (minLowBitsAvailable() - tagBits()) : 0; } using TagTable = std::array; /// Returns the tag lookup table for this union's encoding scheme. static constexpr TagTable getTagTable() { if constexpr (useFixedWidthTags()) { return pointer_union_detail::computeFixedTags(); } else { static_assert( pointer_union_detail::typesInNonDecreasingBitOrder(), "Variable-width PointerUnion types must be in non-decreasing " "NumLowBitsAvailable order"); constexpr auto Table = pointer_union_detail::computeExtendedTags(); static_assert(Table.has_value(), "Too many types for the available low bits"); return *Table; } } // Variable-width isNull: check membership in the sparse set of tag values. // A single threshold comparison does not work here because lower-tier // non-null pointers can encode to values below higher-tier thresholds. template static constexpr bool isNullVariableImpl(uintptr_t V, std::index_sequence) { constexpr TagTable Table = getTagTable(); static_assert(tagShift() == 0, "isNullVariableImpl assumes tag starts at bit 0"); return ((V == Table[Is].Value) || ...); } template static uintptr_t encode(T V) { constexpr TagTable Table = getTagTable(); constexpr int Shift = tagShift(); constexpr size_t Idx = FirstIndexOfType::value; static_assert(Table[0].Value == 0, "First type must have tag value 0 for getAddrOfPtr1"); uintptr_t PtrInt = reinterpret_cast( PointerLikeTypeTraits::getAsVoidPointer(V)); assert((PtrInt & (Table[Idx].Mask << Shift)) == 0 && "Pointer low bits collide with tag"); return PtrInt | (Table[Idx].Value << Shift); } public: PointerUnion() = default; PointerUnion(std::nullptr_t) : PointerUnion() {} using Base::Base; using Base::operator=; /// Assignment from nullptr clears the union, resetting to the first type. const PointerUnion &operator=(std::nullptr_t) { this->Val = uintptr_t(0); return *this; } /// Test if the pointer held in the union is null, regardless of /// which type it is. bool isNull() const { if constexpr (useFixedWidthTags()) { return (static_cast(this->Val.asInt()) >> minLowBitsAvailable()) == 0; } else { return isNullVariableImpl(static_cast(this->Val.asInt()), std::index_sequence_for{}); } } explicit operator bool() const { return !isNull(); } // FIXME: Replace the uses of is(), get() and dyn_cast() with // isa, cast and the llvm::dyn_cast /// Test if the Union currently holds the type matching T. template [[deprecated("Use isa instead")]] bool is() const { return isa(*this); } /// Returns the value of the specified pointer type. /// /// If the specified pointer type is incorrect, assert. template [[deprecated("Use cast instead")]] T get() const { assert(isa(*this) && "Invalid accessor called"); return cast(*this); } /// Returns the current pointer if it is of the specified pointer type, /// otherwise returns null. template inline T dyn_cast() const { return llvm::dyn_cast_if_present(*this); } /// If the union is set to the first pointer type get an address pointing to /// it. First const *getAddrOfPtr1() const { return const_cast(this)->getAddrOfPtr1(); } /// If the union is set to the first pointer type get an address pointing to /// it. First *getAddrOfPtr1() { static_assert(FirstIndexOfType::value == 0, "First type must have tag value 0 for getAddrOfPtr1"); assert(isa(*this) && "Val is not the first pointer"); // tag == 0 for first type, so asInt() is the raw pointer value. assert( PointerLikeTypeTraits::getAsVoidPointer(cast(*this)) == reinterpret_cast(this->Val.asInt()) && "Can't get the address because PointerLikeTypeTraits changes the ptr"); return const_cast( reinterpret_cast(this->Val.getPointerAddress())); } void *getOpaqueValue() const { return reinterpret_cast(this->Val.asInt()); } static inline PointerUnion getFromOpaqueValue(void *VP) { PointerUnion V; V.Val = reinterpret_cast(VP); return V; } friend bool operator==(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() == rhs.getOpaqueValue(); } friend bool operator!=(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() != rhs.getOpaqueValue(); } friend bool operator<(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() < rhs.getOpaqueValue(); } }; // Specialization of CastInfo for PointerUnion. template struct CastInfo> : public DefaultDoCastIfPossible, CastInfo>> { using From = PointerUnion; static inline bool isPossible(From &F) { constexpr std::array Table = From::getTagTable(); constexpr int Shift = From::tagShift(); constexpr size_t Idx = FirstIndexOfType::value; auto V = reinterpret_cast(F.getOpaqueValue()); constexpr uintptr_t TagMask = Table[Idx].Mask << Shift; constexpr uintptr_t TagValue = Table[Idx].Value << Shift; return (V & TagMask) == TagValue; } static To doCast(From &F) { assert(isPossible(F) && "cast to an incompatible type!"); constexpr std::array Table = From::getTagTable(); constexpr int Shift = From::tagShift(); constexpr size_t Idx = FirstIndexOfType::value; constexpr uintptr_t PtrMask = ~(uintptr_t(Table[Idx].Mask) << Shift); void *Ptr = reinterpret_cast( reinterpret_cast(F.getOpaqueValue()) & PtrMask); return PointerLikeTypeTraits::getFromVoidPointer(Ptr); } static inline To castFailed() { return To(); } }; template struct CastInfo> : public ConstStrippingForwardingCast, CastInfo>> { }; // Teach SmallPtrSet that PointerUnion is "basically a pointer". // Spare low bits below the tag are available for nesting. // This specialization is only instantiated when used (lazy), so // PointerLikeTypeTraits / alignof() are not evaluated for // incomplete types. template struct PointerLikeTypeTraits> { using Union = PointerUnion; static inline void *getAsVoidPointer(const Union &P) { return P.getOpaqueValue(); } static inline Union getFromVoidPointer(void *P) { return Union::getFromOpaqueValue(P); } // The number of bits available are the min of the pointer types minus the // bits needed for the discriminator. static constexpr int NumLowBitsAvailable = Union::tagShift(); }; // Teach DenseMap how to use PointerUnions as keys. template struct DenseMapInfo> { using Union = PointerUnion; static unsigned getHashValue(const Union &UnionVal) { auto Key = reinterpret_cast(UnionVal.getOpaqueValue()); return DenseMapInfo::getHashValue(Key); } static bool isEqual(const Union &LHS, const Union &RHS) { return LHS == RHS; } }; } // end namespace llvm #endif // LLVM_ADT_POINTERUNION_H