| //===--- PointerIntEnum.h ---------------------------------------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_BASIC_POINTERINTENUM_H |
| #define SWIFT_BASIC_POINTERINTENUM_H |
| |
| #include "swift/Basic/LLVM.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/Support/PointerLikeTypeTraits.h" |
| #include <cassert> |
| #include <climits> |
| #include <cstdlib> |
| #include <cstring> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace swift { |
| |
| /// A tiny meta function to compute the log2 of a compile time constant. |
| /// |
| /// *NOTE* This will be in an updated version of LLVM so this should be removed |
| /// at that point in time. |
| template <size_t N> |
| struct ConstantLog2 |
| : std::integral_constant<size_t, ConstantLog2<N / 2>::value + 1> {}; |
| template <> struct ConstantLog2<1> : std::integral_constant<size_t, 0> {}; |
| |
| /// A meta function for computing at compile time cleanly the value for an index |
| /// kind's value without using cpp macros. |
| template <unsigned Value, typename EnumTy> |
| struct PointerIntEnumIndexKindValue |
| : std::integral_constant<unsigned, |
| (Value << unsigned(ConstantLog2< |
| size_t(EnumTy::FirstIndexKind) + 1>::value)) | |
| unsigned(EnumTy::FirstIndexKind)> {}; |
| |
| /// A pointer sized ADT that is able to compactly represent a Swift like enum |
| /// that can contain both Integer and Pointer payloads. It attempts to optimize |
| /// for the case of being able to represent as many pointer cases as possible |
| /// while allowing for indices to be stored as well. Without any loss of |
| /// generality assume that T* is our stored pointer. Then this is done as |
| /// follows: |
| /// |
| /// 1. A PointerIntEnum for which bits [0, (num_tagged_bits(T*)-1)] are not all |
| /// set to 1 represent an enum with a pointer case. This means that one can have |
| /// at most ((1 << num_tagged_bits(T*)) - 2) enum cases associated with |
| /// pointers. |
| /// |
| /// 2. A PointerIntEnum for which bits [0, (num_tagged_bits(T*)-1)] are all set |
| /// is either an invalid PointerIntEnum or an index. |
| /// |
| /// 3. A PointerIntEnum with all bits set is an invalid PointerIntEnum. |
| /// |
| /// 4. A PointerIntEnum for which bits [0, (num_tagged_bits(T*)-1)] are all set |
| /// but for which the upper bits are not all set is an index enum. The case bits |
| /// for the index PointerIntEnum are stored in bits [num_tagged_bits(T*), |
| /// num_tagged_bits(T*) + num_index_case_bits]. Then the actual index is stored |
| /// in the remaining top bits. For the case in which this is used in swift |
| /// currently, we use 3 index bits meaning that on a 32 bit system we have 26 |
| /// bits for representing indices meaning we can represent indices up to |
| /// 67_108_862. Any index larger than that will result in an invalid |
| /// PointerIntEnum. On 64 bit we have many more bits than that. |
| /// |
| /// By using this representation, we can make PointerIntEnum a true value type |
| /// that is trivially constructible and destructible without needing to malloc |
| /// memory. |
| /// |
| /// In order for all of this to work, the user of this needs to construct an |
| /// enum with the appropriate case structure that allows the data structure to |
| /// determine what cases are pointer and which are indices. For instance the one |
| /// used by Projection in swift is: |
| /// |
| /// enum class ProjectionKind : unsigned { |
| /// // PointerProjectionKinds |
| /// Upcast = 0, |
| /// RefCast = 1, |
| /// BitwiseCast = 2, |
| /// FirstPointerKind = Upcast, |
| /// LastPointerKind = BitwiseCast, |
| /// |
| /// |
| /// // This needs to be set to ((1 << num_tagged_bits(T*)) - 1). It |
| /// // represents the first NonPointerKind. |
| /// FirstIndexKind = 7, |
| /// |
| /// // Index Projection Kinds |
| /// Struct = PointerIntEnumIndexKindValue<0, EnumTy>::value, |
| /// Tuple = PointerIntEnumIndexKindValue<1, EnumTy>::value, |
| /// Index = PointerIntEnumIndexKindValue<2, EnumTy>::value, |
| /// Class = PointerIntEnumIndexKindValue<3, EnumTy>::value, |
| /// Enum = PointerIntEnumIndexKindValue<4, EnumTy>::value, |
| /// LastIndexKind = Enum, |
| /// }; |
| /// |
| template <typename EnumTy, typename PointerTy, unsigned NumPointerKindBits, |
| unsigned NumIndexKindBits, |
| typename PtrTraits = llvm::PointerLikeTypeTraits<PointerTy>> |
| class PointerIntEnum { |
| |
| // Make sure that the enum fits our requirements. |
| static_assert(unsigned(EnumTy::FirstIndexKind) == |
| ((1U << NumPointerKindBits) - 1U), |
| "Invalid Enum"); |
| static_assert(unsigned(EnumTy::FirstIndexKind) <= |
| unsigned(EnumTy::LastIndexKind), |
| "Invalid Enum"); |
| static_assert(unsigned(EnumTy::FirstPointerKind) <= |
| unsigned(EnumTy::LastPointerKind), |
| "Invalid Enum"); |
| static_assert(unsigned(EnumTy::LastPointerKind) < |
| unsigned(EnumTy::FirstIndexKind), |
| "Invalid Enum"); |
| |
| /// The offset in bits where an index would be stored. |
| static constexpr unsigned IndexShiftOffset = |
| NumIndexKindBits + NumPointerKindBits; |
| |
| /// The number of bits in a PointerIntEnum that can be used to store indices. |
| static constexpr unsigned NumIndexBits = |
| sizeof(uintptr_t) * CHAR_BIT - IndexShiftOffset; |
| |
| /// The maximum index that can be stored for an index PointerIntEnum case. |
| static constexpr uintptr_t MaxIndex = (uintptr_t(1) << NumIndexBits) - 2; |
| |
| /// The bit representation of an Invalid PointerIntEnum's storage. |
| static constexpr uintptr_t InvalidStorage = uintptr_t(0) - 1; |
| |
| /// The pointer sized type used for the actual storage. |
| uintptr_t Storage; |
| |
| public: |
| PointerIntEnum() : Storage(InvalidStorage) {} |
| |
| /// Initialize this PointerIntEnum with the kind \p Kind and the Pointer \p |
| /// Ptr. |
| PointerIntEnum(EnumTy Kind, uintptr_t NewIndex) { |
| // If we can not represent this index, make the PointerIntEnum invalid. |
| if (NewIndex > MaxIndex) { |
| Storage = InvalidStorage; |
| return; |
| } |
| |
| Storage = uintptr_t(Kind) | (NewIndex << IndexShiftOffset); |
| } |
| |
| /// Initialize this PointerIntEnum with the kind \p Kind and the Pointer \p |
| /// Ptr. |
| PointerIntEnum(EnumTy Kind, PointerTy Ptr) { |
| void *VoidPtr = PtrTraits::getAsVoidPointer(Ptr); |
| |
| // Make sure the pointer is at least aligned to NumPointerKindBits. |
| assert((uintptr_t(VoidPtr) & ((1 << NumPointerKindBits) - 1)) == 0); |
| // Make sure that Kind is a PointerKind. |
| assert(unsigned(Kind) >= unsigned(EnumTy::FirstPointerKind)); |
| assert(unsigned(Kind) <= unsigned(EnumTy::LastPointerKind)); |
| |
| Storage = uintptr_t(VoidPtr) | uintptr_t(Kind); |
| } |
| |
| PointerIntEnum(PointerIntEnum &&P) = default; |
| PointerIntEnum(const PointerIntEnum &P) = default; |
| ~PointerIntEnum() = default; |
| PointerIntEnum &operator=(const PointerIntEnum &P) = default; |
| PointerIntEnum &operator=(PointerIntEnum &&P) = default; |
| |
| bool isValid() const { return Storage != InvalidStorage; } |
| |
| bool operator==(const PointerIntEnum &Other) const { |
| // Just compare the raw storage. |
| return Other.Storage == Storage; |
| } |
| |
| bool operator!=(const PointerIntEnum &Other) const { |
| return !(*this == Other); |
| } |
| |
| /// \returns the kind of the enum if the enum is valid. Returns None if the |
| /// enum is invalid. |
| Optional<EnumTy> getKind() const { |
| if (!isValid()) |
| return None; |
| |
| // Check if the bottom pointer bits are all not set. If that is true then we |
| // know that we have a pointer kind. |
| unsigned PointerBits = Storage & uintptr_t(EnumTy::FirstIndexKind); |
| if (PointerBits != unsigned(EnumTy::FirstIndexKind)) { |
| return EnumTy(PointerBits); |
| } |
| |
| // Otherwise, we have an index kind. Just mask off the actual index bits and |
| // return the kind. |
| unsigned Mask = (1 << IndexShiftOffset) - 1; |
| unsigned MaskedStorage = Storage & Mask; |
| return EnumTy(MaskedStorage); |
| } |
| |
| /// \returns the index stored in the enum if the enum has an index |
| /// payload. Asserts if the PointerIntEnum is invalid or has a pointer |
| /// payload. |
| uintptr_t getIndex() const { |
| assert(isValid()); |
| assert(unsigned(*getKind()) >= unsigned(EnumTy::FirstIndexKind)); |
| return Storage >> IndexShiftOffset; |
| } |
| |
| /// \returns the pointer stored in the enum if the enum has a pointer |
| /// payload. Asserts if the PointerIntEnum is invalid or has an index payload. |
| PointerTy getPointer() const { |
| assert(isValid()); |
| assert(unsigned(*getKind()) <= unsigned(EnumTy::LastPointerKind)); |
| uintptr_t Value = Storage & ~(uintptr_t(EnumTy::FirstIndexKind)); |
| return PtrTraits::getFromVoidPointer((void *)(Value)); |
| } |
| |
| /// Return the raw storage of the type. Used for testing purposes. |
| uintptr_t getStorage() const { return Storage; } |
| }; |
| |
| } // end swift namespace |
| |
| #endif // SWIFT_BASIC_POINTERINTENUM_H |