mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-03 03:01:44 +00:00
wpiutil: Add unique_function (#1761)
This is a move-only variant of std::function to support move-only captures. Imported from LLVM with some small tweaks (changed to 4 pointer internal storage, warnings fixes).
This commit is contained in:
@@ -15,6 +15,9 @@
|
||||
#ifndef WPIUTIL_WPI_COMPILER_H
|
||||
#define WPIUTIL_WPI_COMPILER_H
|
||||
|
||||
#include <new>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <sal.h>
|
||||
#endif
|
||||
@@ -468,4 +471,46 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// Allocate a buffer of memory with the given size and alignment.
|
||||
///
|
||||
/// When the compiler supports aligned operator new, this will use it to to
|
||||
/// handle even over-aligned allocations.
|
||||
///
|
||||
/// However, this doesn't make any attempt to leverage the fancier techniques
|
||||
/// like posix_memalign due to portability. It is mostly intended to allow
|
||||
/// compatibility with platforms that, after aligned allocation was added, use
|
||||
/// reduced default alignment.
|
||||
inline void *allocate_buffer(size_t Size, size_t Alignment) {
|
||||
return ::operator new(Size
|
||||
#ifdef __cpp_aligned_new
|
||||
,
|
||||
std::align_val_t(Alignment)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/// Deallocate a buffer of memory with the given size and alignment.
|
||||
///
|
||||
/// If supported, this will used the sized delete operator. Also if supported,
|
||||
/// this will pass the alignment to the delete operator.
|
||||
///
|
||||
/// The pointer must have been allocated with the corresponding new operator,
|
||||
/// most likely using the above helper.
|
||||
inline void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) {
|
||||
::operator delete(Ptr
|
||||
#ifdef __cpp_sized_deallocation
|
||||
,
|
||||
Size
|
||||
#endif
|
||||
#ifdef __cpp_aligned_new
|
||||
,
|
||||
std::align_val_t(Alignment)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
} // End namespace wpi
|
||||
|
||||
#endif
|
||||
|
||||
303
wpiutil/src/main/native/include/wpi/FunctionExtras.h
Normal file
303
wpiutil/src/main/native/include/wpi/FunctionExtras.h
Normal file
@@ -0,0 +1,303 @@
|
||||
//===- FunctionExtras.h - Function type erasure utilities -------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file provides a collection of function (or more generally, callable)
|
||||
/// type erasure utilities supplementing those provided by the standard library
|
||||
/// in `<function>`.
|
||||
///
|
||||
/// It provides `unique_function`, which works like `std::function` but supports
|
||||
/// move-only callable objects.
|
||||
///
|
||||
/// Future plans:
|
||||
/// - Add a `function` that provides const, volatile, and ref-qualified support,
|
||||
/// which doesn't work with `std::function`.
|
||||
/// - Provide support for specifying multiple signatures to type erase callable
|
||||
/// objects with an overload set, such as those produced by generic lambdas.
|
||||
/// - Expand to include a copyable utility that directly replaces std::function
|
||||
/// but brings the above improvements.
|
||||
///
|
||||
/// Note that LLVM's utilities are greatly simplified by not supporting
|
||||
/// allocators.
|
||||
///
|
||||
/// If the standard library ever begins to provide comparable facilities we can
|
||||
/// consider switching to those.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_FUNCTION_EXTRAS_H
|
||||
#define WPIUTIL_WPI_FUNCTION_EXTRAS_H
|
||||
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/PointerIntPair.h"
|
||||
#include "wpi/PointerUnion.h"
|
||||
#include <memory>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
template <typename FunctionT> class unique_function;
|
||||
|
||||
// GCC warns on OutOfLineStorage
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
template <typename ReturnT, typename... ParamTs>
|
||||
class unique_function<ReturnT(ParamTs...)> {
|
||||
static constexpr size_t InlineStorageSize = sizeof(void *) * 4;
|
||||
|
||||
// MSVC has a bug and ICEs if we give it a particular dependent value
|
||||
// expression as part of the `std::conditional` below. To work around this,
|
||||
// we build that into a template struct's constexpr bool.
|
||||
template <typename T> struct IsSizeLessThanThresholdT {
|
||||
static constexpr bool value = sizeof(T) <= (2 * sizeof(void *));
|
||||
};
|
||||
|
||||
// Provide a type function to map parameters that won't observe extra copies
|
||||
// or moves and which are small enough to likely pass in register to values
|
||||
// and all other types to l-value reference types. We use this to compute the
|
||||
// types used in our erased call utility to minimize copies and moves unless
|
||||
// doing so would force things unnecessarily into memory.
|
||||
//
|
||||
// The heuristic used is related to common ABI register passing conventions.
|
||||
// It doesn't have to be exact though, and in one way it is more strict
|
||||
// because we want to still be able to observe either moves *or* copies.
|
||||
template <typename T>
|
||||
using AdjustedParamT = typename std::conditional<
|
||||
!std::is_reference<T>::value &&
|
||||
std::is_trivially_copy_constructible<T>::value &&
|
||||
std::is_trivially_move_constructible<T>::value &&
|
||||
IsSizeLessThanThresholdT<T>::value,
|
||||
T, T &>::type;
|
||||
|
||||
// The type of the erased function pointer we use as a callback to dispatch to
|
||||
// the stored callable when it is trivial to move and destroy.
|
||||
using CallPtrT = ReturnT (*)(void *CallableAddr,
|
||||
AdjustedParamT<ParamTs>... Params);
|
||||
using MovePtrT = void (*)(void *LHSCallableAddr, void *RHSCallableAddr);
|
||||
using DestroyPtrT = void (*)(void *CallableAddr);
|
||||
|
||||
/// A struct to hold a single trivial callback with sufficient alignment for
|
||||
/// our bitpacking.
|
||||
struct alignas(8) TrivialCallback {
|
||||
CallPtrT CallPtr;
|
||||
};
|
||||
|
||||
/// A struct we use to aggregate three callbacks when we need full set of
|
||||
/// operations.
|
||||
struct alignas(8) NonTrivialCallbacks {
|
||||
CallPtrT CallPtr;
|
||||
MovePtrT MovePtr;
|
||||
DestroyPtrT DestroyPtr;
|
||||
};
|
||||
|
||||
// Create a pointer union between either a pointer to a static trivial call
|
||||
// pointer in a struct or a pointer to a static struct of the call, move, and
|
||||
// destroy pointers.
|
||||
using CallbackPointerUnionT =
|
||||
PointerUnion<TrivialCallback *, NonTrivialCallbacks *>;
|
||||
|
||||
// The main storage buffer. This will either have a pointer to out-of-line
|
||||
// storage or an inline buffer storing the callable.
|
||||
union StorageUnionT {
|
||||
// For out-of-line storage we keep a pointer to the underlying storage and
|
||||
// the size. This is enough to deallocate the memory.
|
||||
struct OutOfLineStorageT {
|
||||
void *StoragePtr;
|
||||
size_t Size;
|
||||
size_t Alignment;
|
||||
} OutOfLineStorage;
|
||||
static_assert(
|
||||
sizeof(OutOfLineStorageT) <= InlineStorageSize,
|
||||
"Should always use all of the out-of-line storage for inline storage!");
|
||||
|
||||
// For in-line storage, we just provide an aligned character buffer. We
|
||||
// provide four pointers worth of storage here.
|
||||
typename std::aligned_storage<InlineStorageSize, alignof(void *)>::type
|
||||
InlineStorage;
|
||||
} StorageUnion;
|
||||
|
||||
// A compressed pointer to either our dispatching callback or our table of
|
||||
// dispatching callbacks and the flag for whether the callable itself is
|
||||
// stored inline or not.
|
||||
PointerIntPair<CallbackPointerUnionT, 1, bool> CallbackAndInlineFlag;
|
||||
|
||||
bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); }
|
||||
|
||||
bool isTrivialCallback() const {
|
||||
return CallbackAndInlineFlag.getPointer().template is<TrivialCallback *>();
|
||||
}
|
||||
|
||||
CallPtrT getTrivialCallback() const {
|
||||
return CallbackAndInlineFlag.getPointer().template get<TrivialCallback *>()->CallPtr;
|
||||
}
|
||||
|
||||
NonTrivialCallbacks *getNonTrivialCallbacks() const {
|
||||
return CallbackAndInlineFlag.getPointer()
|
||||
.template get<NonTrivialCallbacks *>();
|
||||
}
|
||||
|
||||
void *getInlineStorage() { return &StorageUnion.InlineStorage; }
|
||||
|
||||
void *getOutOfLineStorage() {
|
||||
return StorageUnion.OutOfLineStorage.StoragePtr;
|
||||
}
|
||||
size_t getOutOfLineStorageSize() const {
|
||||
return StorageUnion.OutOfLineStorage.Size;
|
||||
}
|
||||
size_t getOutOfLineStorageAlignment() const {
|
||||
return StorageUnion.OutOfLineStorage.Alignment;
|
||||
}
|
||||
|
||||
void setOutOfLineStorage(void *Ptr, size_t Size, size_t Alignment) {
|
||||
StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment};
|
||||
}
|
||||
|
||||
template <typename CallableT>
|
||||
static ReturnT CallImpl(void *CallableAddr, AdjustedParamT<ParamTs>... Params) {
|
||||
return (*reinterpret_cast<CallableT *>(CallableAddr))(
|
||||
std::forward<ParamTs>(Params)...);
|
||||
}
|
||||
|
||||
template <typename CallableT>
|
||||
static void MoveImpl(void *LHSCallableAddr, void *RHSCallableAddr) noexcept {
|
||||
new (LHSCallableAddr)
|
||||
CallableT(std::move(*reinterpret_cast<CallableT *>(RHSCallableAddr)));
|
||||
}
|
||||
|
||||
template <typename CallableT>
|
||||
static void DestroyImpl(void *CallableAddr) noexcept {
|
||||
reinterpret_cast<CallableT *>(CallableAddr)->~CallableT();
|
||||
}
|
||||
|
||||
public:
|
||||
unique_function() = default;
|
||||
unique_function(std::nullptr_t /*null_callable*/) {}
|
||||
|
||||
~unique_function() {
|
||||
if (!CallbackAndInlineFlag.getPointer())
|
||||
return;
|
||||
|
||||
// Cache this value so we don't re-check it after type-erased operations.
|
||||
bool IsInlineStorage = isInlineStorage();
|
||||
|
||||
if (!isTrivialCallback())
|
||||
getNonTrivialCallbacks()->DestroyPtr(
|
||||
IsInlineStorage ? getInlineStorage() : getOutOfLineStorage());
|
||||
|
||||
if (!IsInlineStorage)
|
||||
deallocate_buffer(getOutOfLineStorage(), getOutOfLineStorageSize(),
|
||||
getOutOfLineStorageAlignment());
|
||||
}
|
||||
|
||||
unique_function(unique_function &&RHS) noexcept {
|
||||
// Copy the callback and inline flag.
|
||||
CallbackAndInlineFlag = RHS.CallbackAndInlineFlag;
|
||||
|
||||
// If the RHS is empty, just copying the above is sufficient.
|
||||
if (!RHS)
|
||||
return;
|
||||
|
||||
if (!isInlineStorage()) {
|
||||
// The out-of-line case is easiest to move.
|
||||
StorageUnion.OutOfLineStorage = RHS.StorageUnion.OutOfLineStorage;
|
||||
} else if (isTrivialCallback()) {
|
||||
// Move is trivial, just memcpy the bytes across.
|
||||
memcpy(getInlineStorage(), RHS.getInlineStorage(), InlineStorageSize);
|
||||
} else {
|
||||
// Non-trivial move, so dispatch to a type-erased implementation.
|
||||
getNonTrivialCallbacks()->MovePtr(getInlineStorage(),
|
||||
RHS.getInlineStorage());
|
||||
}
|
||||
|
||||
// Clear the old callback and inline flag to get back to as-if-null.
|
||||
RHS.CallbackAndInlineFlag = {};
|
||||
|
||||
#ifndef NDEBUG
|
||||
// In debug builds, we also scribble across the rest of the storage.
|
||||
memset(RHS.getInlineStorage(), 0xAD, InlineStorageSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
unique_function &operator=(unique_function &&RHS) noexcept {
|
||||
if (this == &RHS)
|
||||
return *this;
|
||||
|
||||
// Because we don't try to provide any exception safety guarantees we can
|
||||
// implement move assignment very simply by first destroying the current
|
||||
// object and then move-constructing over top of it.
|
||||
this->~unique_function();
|
||||
new (this) unique_function(std::move(RHS));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename CallableT> unique_function(CallableT Callable) {
|
||||
bool IsInlineStorage = true;
|
||||
void *CallableAddr = getInlineStorage();
|
||||
if (sizeof(CallableT) > InlineStorageSize ||
|
||||
alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) {
|
||||
IsInlineStorage = false;
|
||||
// Allocate out-of-line storage. FIXME: Use an explicit alignment
|
||||
// parameter in C++17 mode.
|
||||
auto Size = sizeof(CallableT);
|
||||
auto Alignment = alignof(CallableT);
|
||||
CallableAddr = allocate_buffer(Size, Alignment);
|
||||
setOutOfLineStorage(CallableAddr, Size, Alignment);
|
||||
}
|
||||
|
||||
// Now move into the storage.
|
||||
new (CallableAddr) CallableT(std::move(Callable));
|
||||
|
||||
// See if we can create a trivial callback. We need the callable to be
|
||||
// trivially moved and trivially destroyed so that we don't have to store
|
||||
// type erased callbacks for those operations.
|
||||
//
|
||||
// FIXME: We should use constexpr if here and below to avoid instantiating
|
||||
// the non-trivial static objects when unnecessary. While the linker should
|
||||
// remove them, it is still wasteful.
|
||||
if (std::is_trivially_move_constructible<CallableT>::value &&
|
||||
std::is_trivially_destructible<CallableT>::value) {
|
||||
// We need to create a nicely aligned object. We use a static variable
|
||||
// for this because it is a trivial struct.
|
||||
static TrivialCallback Callback = { &CallImpl<CallableT> };
|
||||
|
||||
CallbackAndInlineFlag = {&Callback, IsInlineStorage};
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we need to point at an object that contains all the different
|
||||
// type erased behaviors needed. Create a static instance of the struct type
|
||||
// here and then use a pointer to that.
|
||||
static NonTrivialCallbacks Callbacks = {
|
||||
&CallImpl<CallableT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};
|
||||
|
||||
CallbackAndInlineFlag = {&Callbacks, IsInlineStorage};
|
||||
}
|
||||
|
||||
ReturnT operator()(ParamTs... Params) {
|
||||
void *CallableAddr =
|
||||
isInlineStorage() ? getInlineStorage() : getOutOfLineStorage();
|
||||
|
||||
return (isTrivialCallback()
|
||||
? getTrivialCallback()
|
||||
: getNonTrivialCallbacks()->CallPtr)(CallableAddr, Params...);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (bool)CallbackAndInlineFlag.getPointer();
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_FUNCTION_H
|
||||
235
wpiutil/src/main/native/include/wpi/PointerIntPair.h
Normal file
235
wpiutil/src/main/native/include/wpi/PointerIntPair.h
Normal file
@@ -0,0 +1,235 @@
|
||||
//===- llvm/ADT/PointerIntPair.h - Pair for pointer and int -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the PointerIntPair class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_POINTERINTPAIR_H
|
||||
#define WPIUTIL_WPI_POINTERINTPAIR_H
|
||||
|
||||
#include "wpi/PointerLikeTypeTraits.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
template <typename T> struct DenseMapInfo;
|
||||
template <typename PointerT, unsigned IntBits, typename PtrTraits>
|
||||
struct PointerIntPairInfo;
|
||||
|
||||
/// PointerIntPair - This class implements a pair of a pointer and small
|
||||
/// integer. It is designed to represent this in the space required by one
|
||||
/// pointer by bitmangling the integer into the low part of the pointer. This
|
||||
/// can only be done for small integers: typically up to 3 bits, but it depends
|
||||
/// on the number of bits available according to PointerLikeTypeTraits for the
|
||||
/// type.
|
||||
///
|
||||
/// Note that PointerIntPair always puts the IntVal part in the highest bits
|
||||
/// possible. For example, PointerIntPair<void*, 1, bool> will put the bit for
|
||||
/// the bool into bit #2, not bit #0, which allows the low two bits to be used
|
||||
/// for something else. For example, this allows:
|
||||
/// PointerIntPair<PointerIntPair<void*, 1, bool>, 1, bool>
|
||||
/// ... and the two bools will land in different bits.
|
||||
template <typename PointerTy, unsigned IntBits, typename IntType = unsigned,
|
||||
typename PtrTraits = PointerLikeTypeTraits<PointerTy>,
|
||||
typename Info = PointerIntPairInfo<PointerTy, IntBits, PtrTraits>>
|
||||
class PointerIntPair {
|
||||
// Used by MSVC visualizer and generally helpful for debugging/visualizing.
|
||||
using InfoTy = Info;
|
||||
intptr_t Value = 0;
|
||||
|
||||
public:
|
||||
constexpr PointerIntPair() = default;
|
||||
|
||||
PointerIntPair(PointerTy PtrVal, IntType IntVal) {
|
||||
setPointerAndInt(PtrVal, IntVal);
|
||||
}
|
||||
|
||||
explicit PointerIntPair(PointerTy PtrVal) { initWithPointer(PtrVal); }
|
||||
|
||||
PointerTy getPointer() const { return Info::getPointer(Value); }
|
||||
|
||||
IntType getInt() const { return (IntType)Info::getInt(Value); }
|
||||
|
||||
void setPointer(PointerTy PtrVal) {
|
||||
Value = Info::updatePointer(Value, PtrVal);
|
||||
}
|
||||
|
||||
void setInt(IntType IntVal) {
|
||||
Value = Info::updateInt(Value, static_cast<intptr_t>(IntVal));
|
||||
}
|
||||
|
||||
void initWithPointer(PointerTy PtrVal) {
|
||||
Value = Info::updatePointer(0, PtrVal);
|
||||
}
|
||||
|
||||
void setPointerAndInt(PointerTy PtrVal, IntType IntVal) {
|
||||
Value = Info::updateInt(Info::updatePointer(0, PtrVal),
|
||||
static_cast<intptr_t>(IntVal));
|
||||
}
|
||||
|
||||
PointerTy const *getAddrOfPointer() const {
|
||||
return const_cast<PointerIntPair *>(this)->getAddrOfPointer();
|
||||
}
|
||||
|
||||
PointerTy *getAddrOfPointer() {
|
||||
assert(Value == reinterpret_cast<intptr_t>(getPointer()) &&
|
||||
"Can only return the address if IntBits is cleared and "
|
||||
"PtrTraits doesn't change the pointer");
|
||||
return reinterpret_cast<PointerTy *>(&Value);
|
||||
}
|
||||
|
||||
void *getOpaqueValue() const { return reinterpret_cast<void *>(Value); }
|
||||
|
||||
void setFromOpaqueValue(void *Val) {
|
||||
Value = reinterpret_cast<intptr_t>(Val);
|
||||
}
|
||||
|
||||
static PointerIntPair getFromOpaqueValue(void *V) {
|
||||
PointerIntPair P;
|
||||
P.setFromOpaqueValue(V);
|
||||
return P;
|
||||
}
|
||||
|
||||
// Allow PointerIntPairs to be created from const void * if and only if the
|
||||
// pointer type could be created from a const void *.
|
||||
static PointerIntPair getFromOpaqueValue(const void *V) {
|
||||
(void)PtrTraits::getFromVoidPointer(V);
|
||||
return getFromOpaqueValue(const_cast<void *>(V));
|
||||
}
|
||||
|
||||
bool operator==(const PointerIntPair &RHS) const {
|
||||
return Value == RHS.Value;
|
||||
}
|
||||
|
||||
bool operator!=(const PointerIntPair &RHS) const {
|
||||
return Value != RHS.Value;
|
||||
}
|
||||
|
||||
bool operator<(const PointerIntPair &RHS) const { return Value < RHS.Value; }
|
||||
bool operator>(const PointerIntPair &RHS) const { return Value > RHS.Value; }
|
||||
|
||||
bool operator<=(const PointerIntPair &RHS) const {
|
||||
return Value <= RHS.Value;
|
||||
}
|
||||
|
||||
bool operator>=(const PointerIntPair &RHS) const {
|
||||
return Value >= RHS.Value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PointerT, unsigned IntBits, typename PtrTraits>
|
||||
struct PointerIntPairInfo {
|
||||
static_assert(PtrTraits::NumLowBitsAvailable <
|
||||
std::numeric_limits<uintptr_t>::digits,
|
||||
"cannot use a pointer type that has all bits free");
|
||||
static_assert(IntBits <= PtrTraits::NumLowBitsAvailable,
|
||||
"PointerIntPair with integer size too large for pointer");
|
||||
enum : uintptr_t {
|
||||
/// PointerBitMask - The bits that come from the pointer.
|
||||
PointerBitMask =
|
||||
~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1),
|
||||
|
||||
/// IntShift - The number of low bits that we reserve for other uses, and
|
||||
/// keep zero.
|
||||
IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
|
||||
|
||||
/// IntMask - This is the unshifted mask for valid bits of the int type.
|
||||
IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1),
|
||||
|
||||
// ShiftedIntMask - This is the bits for the integer shifted in place.
|
||||
ShiftedIntMask = (uintptr_t)(IntMask << IntShift)
|
||||
};
|
||||
|
||||
static PointerT getPointer(intptr_t Value) {
|
||||
return PtrTraits::getFromVoidPointer(
|
||||
reinterpret_cast<void *>(Value & PointerBitMask));
|
||||
}
|
||||
|
||||
static intptr_t getInt(intptr_t Value) {
|
||||
return (Value >> IntShift) & IntMask;
|
||||
}
|
||||
|
||||
static intptr_t updatePointer(intptr_t OrigValue, PointerT Ptr) {
|
||||
intptr_t PtrWord =
|
||||
reinterpret_cast<intptr_t>(PtrTraits::getAsVoidPointer(Ptr));
|
||||
assert((PtrWord & ~PointerBitMask) == 0 &&
|
||||
"Pointer is not sufficiently aligned");
|
||||
// Preserve all low bits, just update the pointer.
|
||||
return PtrWord | (OrigValue & ~PointerBitMask);
|
||||
}
|
||||
|
||||
static intptr_t updateInt(intptr_t OrigValue, intptr_t Int) {
|
||||
intptr_t IntWord = static_cast<intptr_t>(Int);
|
||||
assert((IntWord & ~IntMask) == 0 && "Integer too large for field");
|
||||
|
||||
// Preserve all bits other than the ones we are updating.
|
||||
return (OrigValue & ~ShiftedIntMask) | IntWord << IntShift;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct isPodLike;
|
||||
template <typename PointerTy, unsigned IntBits, typename IntType>
|
||||
struct isPodLike<PointerIntPair<PointerTy, IntBits, IntType>> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
// Provide specialization of DenseMapInfo for PointerIntPair.
|
||||
template <typename PointerTy, unsigned IntBits, typename IntType>
|
||||
struct DenseMapInfo<PointerIntPair<PointerTy, IntBits, IntType>> {
|
||||
using Ty = PointerIntPair<PointerTy, IntBits, IntType>;
|
||||
|
||||
static Ty getEmptyKey() {
|
||||
uintptr_t Val = static_cast<uintptr_t>(-1);
|
||||
Val <<= PointerLikeTypeTraits<Ty>::NumLowBitsAvailable;
|
||||
return Ty::getFromOpaqueValue(reinterpret_cast<void *>(Val));
|
||||
}
|
||||
|
||||
static Ty getTombstoneKey() {
|
||||
uintptr_t Val = static_cast<uintptr_t>(-2);
|
||||
Val <<= PointerLikeTypeTraits<PointerTy>::NumLowBitsAvailable;
|
||||
return Ty::getFromOpaqueValue(reinterpret_cast<void *>(Val));
|
||||
}
|
||||
|
||||
static unsigned getHashValue(Ty V) {
|
||||
uintptr_t IV = reinterpret_cast<uintptr_t>(V.getOpaqueValue());
|
||||
return unsigned(IV) ^ unsigned(IV >> 9);
|
||||
}
|
||||
|
||||
static bool isEqual(const Ty &LHS, const Ty &RHS) { return LHS == RHS; }
|
||||
};
|
||||
|
||||
// Teach SmallPtrSet that PointerIntPair is "basically a pointer".
|
||||
template <typename PointerTy, unsigned IntBits, typename IntType,
|
||||
typename PtrTraits>
|
||||
struct PointerLikeTypeTraits<
|
||||
PointerIntPair<PointerTy, IntBits, IntType, PtrTraits>> {
|
||||
static inline void *
|
||||
getAsVoidPointer(const PointerIntPair<PointerTy, IntBits, IntType> &P) {
|
||||
return P.getOpaqueValue();
|
||||
}
|
||||
|
||||
static inline PointerIntPair<PointerTy, IntBits, IntType>
|
||||
getFromVoidPointer(void *P) {
|
||||
return PointerIntPair<PointerTy, IntBits, IntType>::getFromOpaqueValue(P);
|
||||
}
|
||||
|
||||
static inline PointerIntPair<PointerTy, IntBits, IntType>
|
||||
getFromVoidPointer(const void *P) {
|
||||
return PointerIntPair<PointerTy, IntBits, IntType>::getFromOpaqueValue(P);
|
||||
}
|
||||
|
||||
enum { NumLowBitsAvailable = PtrTraits::NumLowBitsAvailable - IntBits };
|
||||
};
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_POINTERINTPAIR_H
|
||||
491
wpiutil/src/main/native/include/wpi/PointerUnion.h
Normal file
491
wpiutil/src/main/native/include/wpi/PointerUnion.h
Normal file
@@ -0,0 +1,491 @@
|
||||
//===- llvm/ADT/PointerUnion.h - Discriminated Union of 2 Ptrs --*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the PointerUnion class, which is a discriminated union of
|
||||
// pointer types.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_POINTERUNION_H
|
||||
#define WPIUTIL_WPI_POINTERUNION_H
|
||||
|
||||
#include "wpi/DenseMapInfo.h"
|
||||
#include "wpi/PointerIntPair.h"
|
||||
#include "wpi/PointerLikeTypeTraits.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
template <typename T> struct PointerUnionTypeSelectorReturn {
|
||||
using Return = T;
|
||||
};
|
||||
|
||||
/// Get a type based on whether two types are the same or not.
|
||||
///
|
||||
/// For:
|
||||
///
|
||||
/// \code
|
||||
/// using Ret = typename PointerUnionTypeSelector<T1, T2, EQ, NE>::Return;
|
||||
/// \endcode
|
||||
///
|
||||
/// Ret will be EQ type if T1 is same as T2 or NE type otherwise.
|
||||
template <typename T1, typename T2, typename RET_EQ, typename RET_NE>
|
||||
struct PointerUnionTypeSelector {
|
||||
using Return = typename PointerUnionTypeSelectorReturn<RET_NE>::Return;
|
||||
};
|
||||
|
||||
template <typename T, typename RET_EQ, typename RET_NE>
|
||||
struct PointerUnionTypeSelector<T, T, RET_EQ, RET_NE> {
|
||||
using Return = typename PointerUnionTypeSelectorReturn<RET_EQ>::Return;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2, typename RET_EQ, typename RET_NE>
|
||||
struct PointerUnionTypeSelectorReturn<
|
||||
PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>> {
|
||||
using Return =
|
||||
typename PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>::Return;
|
||||
};
|
||||
|
||||
/// Provide PointerLikeTypeTraits for void* that is used by PointerUnion
|
||||
/// for the two template arguments.
|
||||
template <typename PT1, typename PT2> class PointerUnionUIntTraits {
|
||||
public:
|
||||
static inline void *getAsVoidPointer(void *P) { return P; }
|
||||
static inline void *getFromVoidPointer(void *P) { return P; }
|
||||
|
||||
enum {
|
||||
PT1BitsAv = (int)(PointerLikeTypeTraits<PT1>::NumLowBitsAvailable),
|
||||
PT2BitsAv = (int)(PointerLikeTypeTraits<PT2>::NumLowBitsAvailable),
|
||||
NumLowBitsAvailable = PT1BitsAv < PT2BitsAv ? PT1BitsAv : PT2BitsAv
|
||||
};
|
||||
};
|
||||
|
||||
/// A discriminated union of two pointer types, with the discriminator in the
|
||||
/// low bit 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.
|
||||
///
|
||||
/// Common use patterns would be something like this:
|
||||
/// PointerUnion<int*, float*> P;
|
||||
/// P = (int*)0;
|
||||
/// printf("%d %d", P.is<int*>(), P.is<float*>()); // prints "1 0"
|
||||
/// X = P.get<int*>(); // ok.
|
||||
/// Y = P.get<float*>(); // runtime assertion failure.
|
||||
/// Z = P.get<double*>(); // compile time failure.
|
||||
/// P = (float*)0;
|
||||
/// Y = P.get<float*>(); // ok.
|
||||
/// X = P.get<int*>(); // runtime assertion failure.
|
||||
template <typename PT1, typename PT2> class PointerUnion {
|
||||
public:
|
||||
using ValTy =
|
||||
PointerIntPair<void *, 1, bool, PointerUnionUIntTraits<PT1, PT2>>;
|
||||
|
||||
private:
|
||||
ValTy Val;
|
||||
|
||||
struct IsPT1 {
|
||||
static const int Num = 0;
|
||||
};
|
||||
struct IsPT2 {
|
||||
static const int Num = 1;
|
||||
};
|
||||
template <typename T> struct UNION_DOESNT_CONTAIN_TYPE {};
|
||||
|
||||
public:
|
||||
PointerUnion() = default;
|
||||
PointerUnion(PT1 V)
|
||||
: Val(const_cast<void *>(
|
||||
PointerLikeTypeTraits<PT1>::getAsVoidPointer(V))) {}
|
||||
PointerUnion(PT2 V)
|
||||
: Val(const_cast<void *>(PointerLikeTypeTraits<PT2>::getAsVoidPointer(V)),
|
||||
1) {}
|
||||
|
||||
/// Test if the pointer held in the union is null, regardless of
|
||||
/// which type it is.
|
||||
bool isNull() const {
|
||||
// Convert from the void* to one of the pointer types, to make sure that
|
||||
// we recursively strip off low bits if we have a nested PointerUnion.
|
||||
return !PointerLikeTypeTraits<PT1>::getFromVoidPointer(Val.getPointer());
|
||||
}
|
||||
|
||||
explicit operator bool() const { return !isNull(); }
|
||||
|
||||
/// Test if the Union currently holds the type matching T.
|
||||
template <typename T> int is() const {
|
||||
using Ty = typename ::wpi::PointerUnionTypeSelector<
|
||||
PT1, T, IsPT1,
|
||||
::wpi::PointerUnionTypeSelector<PT2, T, IsPT2,
|
||||
UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
|
||||
int TyNo = Ty::Num;
|
||||
return static_cast<int>(Val.getInt()) == TyNo;
|
||||
}
|
||||
|
||||
/// Returns the value of the specified pointer type.
|
||||
///
|
||||
/// If the specified pointer type is incorrect, assert.
|
||||
template <typename T> T get() const {
|
||||
assert(is<T>() && "Invalid accessor called");
|
||||
return PointerLikeTypeTraits<T>::getFromVoidPointer(Val.getPointer());
|
||||
}
|
||||
|
||||
/// Returns the current pointer if it is of the specified pointer type,
|
||||
/// otherwises returns null.
|
||||
template <typename T> T dyn_cast() const {
|
||||
if (is<T>())
|
||||
return get<T>();
|
||||
return T();
|
||||
}
|
||||
|
||||
/// If the union is set to the first pointer type get an address pointing to
|
||||
/// it.
|
||||
PT1 const *getAddrOfPtr1() const {
|
||||
return const_cast<PointerUnion *>(this)->getAddrOfPtr1();
|
||||
}
|
||||
|
||||
/// If the union is set to the first pointer type get an address pointing to
|
||||
/// it.
|
||||
PT1 *getAddrOfPtr1() {
|
||||
assert(is<PT1>() && "Val is not the first pointer");
|
||||
assert(
|
||||
get<PT1>() == Val.getPointer() &&
|
||||
"Can't get the address because PointerLikeTypeTraits changes the ptr");
|
||||
return const_cast<PT1 *>(
|
||||
reinterpret_cast<const PT1 *>(Val.getAddrOfPointer()));
|
||||
}
|
||||
|
||||
/// Assignment from nullptr which just clears the union.
|
||||
const PointerUnion &operator=(std::nullptr_t) {
|
||||
Val.initWithPointer(nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assignment operators - Allow assigning into this union from either
|
||||
/// pointer type, setting the discriminator to remember what it came from.
|
||||
const PointerUnion &operator=(const PT1 &RHS) {
|
||||
Val.initWithPointer(
|
||||
const_cast<void *>(PointerLikeTypeTraits<PT1>::getAsVoidPointer(RHS)));
|
||||
return *this;
|
||||
}
|
||||
const PointerUnion &operator=(const PT2 &RHS) {
|
||||
Val.setPointerAndInt(
|
||||
const_cast<void *>(PointerLikeTypeTraits<PT2>::getAsVoidPointer(RHS)),
|
||||
1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void *getOpaqueValue() const { return Val.getOpaqueValue(); }
|
||||
static inline PointerUnion getFromOpaqueValue(void *VP) {
|
||||
PointerUnion V;
|
||||
V.Val = ValTy::getFromOpaqueValue(VP);
|
||||
return V;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PT1, typename PT2>
|
||||
bool operator==(PointerUnion<PT1, PT2> lhs, PointerUnion<PT1, PT2> rhs) {
|
||||
return lhs.getOpaqueValue() == rhs.getOpaqueValue();
|
||||
}
|
||||
|
||||
template <typename PT1, typename PT2>
|
||||
bool operator!=(PointerUnion<PT1, PT2> lhs, PointerUnion<PT1, PT2> rhs) {
|
||||
return lhs.getOpaqueValue() != rhs.getOpaqueValue();
|
||||
}
|
||||
|
||||
template <typename PT1, typename PT2>
|
||||
bool operator<(PointerUnion<PT1, PT2> lhs, PointerUnion<PT1, PT2> rhs) {
|
||||
return lhs.getOpaqueValue() < rhs.getOpaqueValue();
|
||||
}
|
||||
|
||||
// Teach SmallPtrSet that PointerUnion is "basically a pointer", that has
|
||||
// # low bits available = min(PT1bits,PT2bits)-1.
|
||||
template <typename PT1, typename PT2>
|
||||
struct PointerLikeTypeTraits<PointerUnion<PT1, PT2>> {
|
||||
static inline void *getAsVoidPointer(const PointerUnion<PT1, PT2> &P) {
|
||||
return P.getOpaqueValue();
|
||||
}
|
||||
|
||||
static inline PointerUnion<PT1, PT2> getFromVoidPointer(void *P) {
|
||||
return PointerUnion<PT1, PT2>::getFromOpaqueValue(P);
|
||||
}
|
||||
|
||||
// The number of bits available are the min of the two pointer types.
|
||||
enum {
|
||||
NumLowBitsAvailable = PointerLikeTypeTraits<
|
||||
typename PointerUnion<PT1, PT2>::ValTy>::NumLowBitsAvailable
|
||||
};
|
||||
};
|
||||
|
||||
/// A pointer union of three pointer types. See documentation for PointerUnion
|
||||
/// for usage.
|
||||
template <typename PT1, typename PT2, typename PT3> class PointerUnion3 {
|
||||
public:
|
||||
using InnerUnion = PointerUnion<PT1, PT2>;
|
||||
using ValTy = PointerUnion<InnerUnion, PT3>;
|
||||
|
||||
private:
|
||||
ValTy Val;
|
||||
|
||||
struct IsInnerUnion {
|
||||
ValTy Val;
|
||||
|
||||
IsInnerUnion(ValTy val) : Val(val) {}
|
||||
|
||||
template <typename T> int is() const {
|
||||
return Val.template is<InnerUnion>() &&
|
||||
Val.template get<InnerUnion>().template is<T>();
|
||||
}
|
||||
|
||||
template <typename T> T get() const {
|
||||
return Val.template get<InnerUnion>().template get<T>();
|
||||
}
|
||||
};
|
||||
|
||||
struct IsPT3 {
|
||||
ValTy Val;
|
||||
|
||||
IsPT3(ValTy val) : Val(val) {}
|
||||
|
||||
template <typename T> int is() const { return Val.template is<T>(); }
|
||||
template <typename T> T get() const { return Val.template get<T>(); }
|
||||
};
|
||||
|
||||
public:
|
||||
PointerUnion3() = default;
|
||||
PointerUnion3(PT1 V) { Val = InnerUnion(V); }
|
||||
PointerUnion3(PT2 V) { Val = InnerUnion(V); }
|
||||
PointerUnion3(PT3 V) { Val = V; }
|
||||
|
||||
/// Test if the pointer held in the union is null, regardless of
|
||||
/// which type it is.
|
||||
bool isNull() const { return Val.isNull(); }
|
||||
explicit operator bool() const { return !isNull(); }
|
||||
|
||||
/// Test if the Union currently holds the type matching T.
|
||||
template <typename T> int is() const {
|
||||
// If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3.
|
||||
using Ty = typename ::wpi::PointerUnionTypeSelector<
|
||||
PT1, T, IsInnerUnion,
|
||||
::wpi::PointerUnionTypeSelector<PT2, T, IsInnerUnion, IsPT3>>::Return;
|
||||
return Ty(Val).template is<T>();
|
||||
}
|
||||
|
||||
/// Returns the value of the specified pointer type.
|
||||
///
|
||||
/// If the specified pointer type is incorrect, assert.
|
||||
template <typename T> T get() const {
|
||||
assert(is<T>() && "Invalid accessor called");
|
||||
// If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3.
|
||||
using Ty = typename ::wpi::PointerUnionTypeSelector<
|
||||
PT1, T, IsInnerUnion,
|
||||
::wpi::PointerUnionTypeSelector<PT2, T, IsInnerUnion, IsPT3>>::Return;
|
||||
return Ty(Val).template get<T>();
|
||||
}
|
||||
|
||||
/// Returns the current pointer if it is of the specified pointer type,
|
||||
/// otherwises returns null.
|
||||
template <typename T> T dyn_cast() const {
|
||||
if (is<T>())
|
||||
return get<T>();
|
||||
return T();
|
||||
}
|
||||
|
||||
/// Assignment from nullptr which just clears the union.
|
||||
const PointerUnion3 &operator=(std::nullptr_t) {
|
||||
Val = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assignment operators - Allow assigning into this union from either
|
||||
/// pointer type, setting the discriminator to remember what it came from.
|
||||
const PointerUnion3 &operator=(const PT1 &RHS) {
|
||||
Val = InnerUnion(RHS);
|
||||
return *this;
|
||||
}
|
||||
const PointerUnion3 &operator=(const PT2 &RHS) {
|
||||
Val = InnerUnion(RHS);
|
||||
return *this;
|
||||
}
|
||||
const PointerUnion3 &operator=(const PT3 &RHS) {
|
||||
Val = RHS;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void *getOpaqueValue() const { return Val.getOpaqueValue(); }
|
||||
static inline PointerUnion3 getFromOpaqueValue(void *VP) {
|
||||
PointerUnion3 V;
|
||||
V.Val = ValTy::getFromOpaqueValue(VP);
|
||||
return V;
|
||||
}
|
||||
};
|
||||
|
||||
// Teach SmallPtrSet that PointerUnion3 is "basically a pointer", that has
|
||||
// # low bits available = min(PT1bits,PT2bits,PT2bits)-2.
|
||||
template <typename PT1, typename PT2, typename PT3>
|
||||
struct PointerLikeTypeTraits<PointerUnion3<PT1, PT2, PT3>> {
|
||||
static inline void *getAsVoidPointer(const PointerUnion3<PT1, PT2, PT3> &P) {
|
||||
return P.getOpaqueValue();
|
||||
}
|
||||
|
||||
static inline PointerUnion3<PT1, PT2, PT3> getFromVoidPointer(void *P) {
|
||||
return PointerUnion3<PT1, PT2, PT3>::getFromOpaqueValue(P);
|
||||
}
|
||||
|
||||
// The number of bits available are the min of the two pointer types.
|
||||
enum {
|
||||
NumLowBitsAvailable = PointerLikeTypeTraits<
|
||||
typename PointerUnion3<PT1, PT2, PT3>::ValTy>::NumLowBitsAvailable
|
||||
};
|
||||
};
|
||||
|
||||
template <typename PT1, typename PT2, typename PT3>
|
||||
bool operator<(PointerUnion3<PT1, PT2, PT3> lhs,
|
||||
PointerUnion3<PT1, PT2, PT3> rhs) {
|
||||
return lhs.getOpaqueValue() < rhs.getOpaqueValue();
|
||||
}
|
||||
|
||||
/// A pointer union of four pointer types. See documentation for PointerUnion
|
||||
/// for usage.
|
||||
template <typename PT1, typename PT2, typename PT3, typename PT4>
|
||||
class PointerUnion4 {
|
||||
public:
|
||||
using InnerUnion1 = PointerUnion<PT1, PT2>;
|
||||
using InnerUnion2 = PointerUnion<PT3, PT4>;
|
||||
using ValTy = PointerUnion<InnerUnion1, InnerUnion2>;
|
||||
|
||||
private:
|
||||
ValTy Val;
|
||||
|
||||
public:
|
||||
PointerUnion4() = default;
|
||||
PointerUnion4(PT1 V) { Val = InnerUnion1(V); }
|
||||
PointerUnion4(PT2 V) { Val = InnerUnion1(V); }
|
||||
PointerUnion4(PT3 V) { Val = InnerUnion2(V); }
|
||||
PointerUnion4(PT4 V) { Val = InnerUnion2(V); }
|
||||
|
||||
/// Test if the pointer held in the union is null, regardless of
|
||||
/// which type it is.
|
||||
bool isNull() const { return Val.isNull(); }
|
||||
explicit operator bool() const { return !isNull(); }
|
||||
|
||||
/// Test if the Union currently holds the type matching T.
|
||||
template <typename T> int is() const {
|
||||
// If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2.
|
||||
using Ty = typename ::wpi::PointerUnionTypeSelector<
|
||||
PT1, T, InnerUnion1,
|
||||
::wpi::PointerUnionTypeSelector<PT2, T, InnerUnion1,
|
||||
InnerUnion2>>::Return;
|
||||
return Val.template is<Ty>() && Val.template get<Ty>().template is<T>();
|
||||
}
|
||||
|
||||
/// Returns the value of the specified pointer type.
|
||||
///
|
||||
/// If the specified pointer type is incorrect, assert.
|
||||
template <typename T> T get() const {
|
||||
assert(is<T>() && "Invalid accessor called");
|
||||
// If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2.
|
||||
using Ty = typename ::wpi::PointerUnionTypeSelector<
|
||||
PT1, T, InnerUnion1,
|
||||
::wpi::PointerUnionTypeSelector<PT2, T, InnerUnion1,
|
||||
InnerUnion2>>::Return;
|
||||
return Val.template get<Ty>().template get<T>();
|
||||
}
|
||||
|
||||
/// Returns the current pointer if it is of the specified pointer type,
|
||||
/// otherwises returns null.
|
||||
template <typename T> T dyn_cast() const {
|
||||
if (is<T>())
|
||||
return get<T>();
|
||||
return T();
|
||||
}
|
||||
|
||||
/// Assignment from nullptr which just clears the union.
|
||||
const PointerUnion4 &operator=(std::nullptr_t) {
|
||||
Val = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assignment operators - Allow assigning into this union from either
|
||||
/// pointer type, setting the discriminator to remember what it came from.
|
||||
const PointerUnion4 &operator=(const PT1 &RHS) {
|
||||
Val = InnerUnion1(RHS);
|
||||
return *this;
|
||||
}
|
||||
const PointerUnion4 &operator=(const PT2 &RHS) {
|
||||
Val = InnerUnion1(RHS);
|
||||
return *this;
|
||||
}
|
||||
const PointerUnion4 &operator=(const PT3 &RHS) {
|
||||
Val = InnerUnion2(RHS);
|
||||
return *this;
|
||||
}
|
||||
const PointerUnion4 &operator=(const PT4 &RHS) {
|
||||
Val = InnerUnion2(RHS);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void *getOpaqueValue() const { return Val.getOpaqueValue(); }
|
||||
static inline PointerUnion4 getFromOpaqueValue(void *VP) {
|
||||
PointerUnion4 V;
|
||||
V.Val = ValTy::getFromOpaqueValue(VP);
|
||||
return V;
|
||||
}
|
||||
};
|
||||
|
||||
// Teach SmallPtrSet that PointerUnion4 is "basically a pointer", that has
|
||||
// # low bits available = min(PT1bits,PT2bits,PT2bits)-2.
|
||||
template <typename PT1, typename PT2, typename PT3, typename PT4>
|
||||
struct PointerLikeTypeTraits<PointerUnion4<PT1, PT2, PT3, PT4>> {
|
||||
static inline void *
|
||||
getAsVoidPointer(const PointerUnion4<PT1, PT2, PT3, PT4> &P) {
|
||||
return P.getOpaqueValue();
|
||||
}
|
||||
|
||||
static inline PointerUnion4<PT1, PT2, PT3, PT4> getFromVoidPointer(void *P) {
|
||||
return PointerUnion4<PT1, PT2, PT3, PT4>::getFromOpaqueValue(P);
|
||||
}
|
||||
|
||||
// The number of bits available are the min of the two pointer types.
|
||||
enum {
|
||||
NumLowBitsAvailable = PointerLikeTypeTraits<
|
||||
typename PointerUnion4<PT1, PT2, PT3, PT4>::ValTy>::NumLowBitsAvailable
|
||||
};
|
||||
};
|
||||
|
||||
// Teach DenseMap how to use PointerUnions as keys.
|
||||
template <typename T, typename U> struct DenseMapInfo<PointerUnion<T, U>> {
|
||||
using Pair = PointerUnion<T, U>;
|
||||
using FirstInfo = DenseMapInfo<T>;
|
||||
using SecondInfo = DenseMapInfo<U>;
|
||||
|
||||
static inline Pair getEmptyKey() { return Pair(FirstInfo::getEmptyKey()); }
|
||||
|
||||
static inline Pair getTombstoneKey() {
|
||||
return Pair(FirstInfo::getTombstoneKey());
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const Pair &PairVal) {
|
||||
intptr_t key = (intptr_t)PairVal.getOpaqueValue();
|
||||
return DenseMapInfo<intptr_t>::getHashValue(key);
|
||||
}
|
||||
|
||||
static bool isEqual(const Pair &LHS, const Pair &RHS) {
|
||||
return LHS.template is<T>() == RHS.template is<T>() &&
|
||||
(LHS.template is<T>() ? FirstInfo::isEqual(LHS.template get<T>(),
|
||||
RHS.template get<T>())
|
||||
: SecondInfo::isEqual(LHS.template get<U>(),
|
||||
RHS.template get<U>()));
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_POINTERUNION_H
|
||||
Reference in New Issue
Block a user