mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
- Added missing ifdef guard in PointerIntEnum header - Consistent naming convention for ifdef guards - Consistent 'end namespace swift' - Consistent single EOL at end of header files
231 lines
8.9 KiB
C++
231 lines
8.9 KiB
C++
//===--- PointerIntEnum.h ---------------------------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://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
|