diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 4febea193a..f79127af80 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -28,6 +28,7 @@ generatedFileExclude { src/main/native/include/wpi/ErrorOr\.h$ src/main/native/include/wpi/FileSystem\.h$ src/main/native/include/wpi/Format\.h$ + src/main/native/include/wpi/FunctionExtras\.h$ src/main/native/include/wpi/Hashing\.h$ src/main/native/include/wpi/IntrusiveRefCntPtr\.h$ src/main/native/include/wpi/ManagedStatic\.h$ @@ -36,7 +37,9 @@ generatedFileExclude { src/main/native/include/wpi/MemAlloc\.h$ src/main/native/include/wpi/NativeFormatting\.h$ src/main/native/include/wpi/Path\.h$ + src/main/native/include/wpi/PointerIntPair\.h$ src/main/native/include/wpi/PointerLikeTypeTraits\.h$ + src/main/native/include/wpi/PointerUnion\.h$ src/main/native/include/wpi/STLExtras\.h$ src/main/native/include/wpi/Signal\.h$ src/main/native/include/wpi/SmallPtrSet\.h$ @@ -64,6 +67,7 @@ generatedFileExclude { src/main/native/libuv/ src/main/native/resources/ src/test/native/cpp/UnitsTest\.cpp$ + src/test/native/cpp/llvm/ } licenseUpdateExclude { diff --git a/wpiutil/src/main/native/include/wpi/Compiler.h b/wpiutil/src/main/native/include/wpi/Compiler.h index 736166396b..5ceb605983 100644 --- a/wpiutil/src/main/native/include/wpi/Compiler.h +++ b/wpiutil/src/main/native/include/wpi/Compiler.h @@ -15,6 +15,9 @@ #ifndef WPIUTIL_WPI_COMPILER_H #define WPIUTIL_WPI_COMPILER_H +#include +#include + #if defined(_MSC_VER) #include #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 diff --git a/wpiutil/src/main/native/include/wpi/FunctionExtras.h b/wpiutil/src/main/native/include/wpi/FunctionExtras.h new file mode 100644 index 0000000000..8e45cda0e5 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/FunctionExtras.h @@ -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 ``. +/// +/// 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 + +namespace wpi { + +template class unique_function; + +// GCC warns on OutOfLineStorage +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +template +class unique_function { + 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 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 + using AdjustedParamT = typename std::conditional< + !std::is_reference::value && + std::is_trivially_copy_constructible::value && + std::is_trivially_move_constructible::value && + IsSizeLessThanThresholdT::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... 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; + + // 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::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 CallbackAndInlineFlag; + + bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); } + + bool isTrivialCallback() const { + return CallbackAndInlineFlag.getPointer().template is(); + } + + CallPtrT getTrivialCallback() const { + return CallbackAndInlineFlag.getPointer().template get()->CallPtr; + } + + NonTrivialCallbacks *getNonTrivialCallbacks() const { + return CallbackAndInlineFlag.getPointer() + .template get(); + } + + 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 + static ReturnT CallImpl(void *CallableAddr, AdjustedParamT... Params) { + return (*reinterpret_cast(CallableAddr))( + std::forward(Params)...); + } + + template + static void MoveImpl(void *LHSCallableAddr, void *RHSCallableAddr) noexcept { + new (LHSCallableAddr) + CallableT(std::move(*reinterpret_cast(RHSCallableAddr))); + } + + template + static void DestroyImpl(void *CallableAddr) noexcept { + reinterpret_cast(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 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::value && + std::is_trivially_destructible::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 }; + + 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, &MoveImpl, &DestroyImpl}; + + 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 diff --git a/wpiutil/src/main/native/include/wpi/PointerIntPair.h b/wpiutil/src/main/native/include/wpi/PointerIntPair.h new file mode 100644 index 0000000000..e6a1212787 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/PointerIntPair.h @@ -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 +#include +#include + +namespace wpi { + +template struct DenseMapInfo; +template +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 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, 1, bool> +/// ... and the two bools will land in different bits. +template , + typename Info = PointerIntPairInfo> +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(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(IntVal)); + } + + PointerTy const *getAddrOfPointer() const { + return const_cast(this)->getAddrOfPointer(); + } + + PointerTy *getAddrOfPointer() { + assert(Value == reinterpret_cast(getPointer()) && + "Can only return the address if IntBits is cleared and " + "PtrTraits doesn't change the pointer"); + return reinterpret_cast(&Value); + } + + void *getOpaqueValue() const { return reinterpret_cast(Value); } + + void setFromOpaqueValue(void *Val) { + Value = reinterpret_cast(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(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 +struct PointerIntPairInfo { + static_assert(PtrTraits::NumLowBitsAvailable < + std::numeric_limits::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(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(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(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 struct isPodLike; +template +struct isPodLike> { + static const bool value = true; +}; + +// Provide specialization of DenseMapInfo for PointerIntPair. +template +struct DenseMapInfo> { + using Ty = PointerIntPair; + + static Ty getEmptyKey() { + uintptr_t Val = static_cast(-1); + Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; + return Ty::getFromOpaqueValue(reinterpret_cast(Val)); + } + + static Ty getTombstoneKey() { + uintptr_t Val = static_cast(-2); + Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; + return Ty::getFromOpaqueValue(reinterpret_cast(Val)); + } + + static unsigned getHashValue(Ty V) { + uintptr_t IV = reinterpret_cast(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 +struct PointerLikeTypeTraits< + PointerIntPair> { + static inline void * + getAsVoidPointer(const PointerIntPair &P) { + return P.getOpaqueValue(); + } + + static inline PointerIntPair + getFromVoidPointer(void *P) { + return PointerIntPair::getFromOpaqueValue(P); + } + + static inline PointerIntPair + getFromVoidPointer(const void *P) { + return PointerIntPair::getFromOpaqueValue(P); + } + + enum { NumLowBitsAvailable = PtrTraits::NumLowBitsAvailable - IntBits }; +}; + +} // end namespace wpi + +#endif // WPIUTIL_WPI_POINTERINTPAIR_H diff --git a/wpiutil/src/main/native/include/wpi/PointerUnion.h b/wpiutil/src/main/native/include/wpi/PointerUnion.h new file mode 100644 index 0000000000..8d6e580bae --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/PointerUnion.h @@ -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 +#include +#include + +namespace wpi { + +template struct PointerUnionTypeSelectorReturn { + using Return = T; +}; + +/// Get a type based on whether two types are the same or not. +/// +/// For: +/// +/// \code +/// using Ret = typename PointerUnionTypeSelector::Return; +/// \endcode +/// +/// Ret will be EQ type if T1 is same as T2 or NE type otherwise. +template +struct PointerUnionTypeSelector { + using Return = typename PointerUnionTypeSelectorReturn::Return; +}; + +template +struct PointerUnionTypeSelector { + using Return = typename PointerUnionTypeSelectorReturn::Return; +}; + +template +struct PointerUnionTypeSelectorReturn< + PointerUnionTypeSelector> { + using Return = + typename PointerUnionTypeSelector::Return; +}; + +/// Provide PointerLikeTypeTraits for void* that is used by PointerUnion +/// for the two template arguments. +template class PointerUnionUIntTraits { +public: + static inline void *getAsVoidPointer(void *P) { return P; } + static inline void *getFromVoidPointer(void *P) { return P; } + + enum { + PT1BitsAv = (int)(PointerLikeTypeTraits::NumLowBitsAvailable), + PT2BitsAv = (int)(PointerLikeTypeTraits::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 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. +template class PointerUnion { +public: + using ValTy = + PointerIntPair>; + +private: + ValTy Val; + + struct IsPT1 { + static const int Num = 0; + }; + struct IsPT2 { + static const int Num = 1; + }; + template struct UNION_DOESNT_CONTAIN_TYPE {}; + +public: + PointerUnion() = default; + PointerUnion(PT1 V) + : Val(const_cast( + PointerLikeTypeTraits::getAsVoidPointer(V))) {} + PointerUnion(PT2 V) + : Val(const_cast(PointerLikeTypeTraits::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::getFromVoidPointer(Val.getPointer()); + } + + explicit operator bool() const { return !isNull(); } + + /// Test if the Union currently holds the type matching T. + template int is() const { + using Ty = typename ::wpi::PointerUnionTypeSelector< + PT1, T, IsPT1, + ::wpi::PointerUnionTypeSelector>>::Return; + int TyNo = Ty::Num; + return static_cast(Val.getInt()) == TyNo; + } + + /// Returns the value of the specified pointer type. + /// + /// If the specified pointer type is incorrect, assert. + template T get() const { + assert(is() && "Invalid accessor called"); + return PointerLikeTypeTraits::getFromVoidPointer(Val.getPointer()); + } + + /// Returns the current pointer if it is of the specified pointer type, + /// otherwises returns null. + template T dyn_cast() const { + if (is()) + return get(); + 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(this)->getAddrOfPtr1(); + } + + /// If the union is set to the first pointer type get an address pointing to + /// it. + PT1 *getAddrOfPtr1() { + assert(is() && "Val is not the first pointer"); + assert( + get() == Val.getPointer() && + "Can't get the address because PointerLikeTypeTraits changes the ptr"); + return const_cast( + reinterpret_cast(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(PointerLikeTypeTraits::getAsVoidPointer(RHS))); + return *this; + } + const PointerUnion &operator=(const PT2 &RHS) { + Val.setPointerAndInt( + const_cast(PointerLikeTypeTraits::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 +bool operator==(PointerUnion lhs, PointerUnion rhs) { + return lhs.getOpaqueValue() == rhs.getOpaqueValue(); +} + +template +bool operator!=(PointerUnion lhs, PointerUnion rhs) { + return lhs.getOpaqueValue() != rhs.getOpaqueValue(); +} + +template +bool operator<(PointerUnion lhs, PointerUnion rhs) { + return lhs.getOpaqueValue() < rhs.getOpaqueValue(); +} + +// Teach SmallPtrSet that PointerUnion is "basically a pointer", that has +// # low bits available = min(PT1bits,PT2bits)-1. +template +struct PointerLikeTypeTraits> { + static inline void *getAsVoidPointer(const PointerUnion &P) { + return P.getOpaqueValue(); + } + + static inline PointerUnion getFromVoidPointer(void *P) { + return PointerUnion::getFromOpaqueValue(P); + } + + // The number of bits available are the min of the two pointer types. + enum { + NumLowBitsAvailable = PointerLikeTypeTraits< + typename PointerUnion::ValTy>::NumLowBitsAvailable + }; +}; + +/// A pointer union of three pointer types. See documentation for PointerUnion +/// for usage. +template class PointerUnion3 { +public: + using InnerUnion = PointerUnion; + using ValTy = PointerUnion; + +private: + ValTy Val; + + struct IsInnerUnion { + ValTy Val; + + IsInnerUnion(ValTy val) : Val(val) {} + + template int is() const { + return Val.template is() && + Val.template get().template is(); + } + + template T get() const { + return Val.template get().template get(); + } + }; + + struct IsPT3 { + ValTy Val; + + IsPT3(ValTy val) : Val(val) {} + + template int is() const { return Val.template is(); } + template T get() const { return Val.template get(); } + }; + +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 int is() const { + // If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3. + using Ty = typename ::wpi::PointerUnionTypeSelector< + PT1, T, IsInnerUnion, + ::wpi::PointerUnionTypeSelector>::Return; + return Ty(Val).template is(); + } + + /// Returns the value of the specified pointer type. + /// + /// If the specified pointer type is incorrect, assert. + template T get() const { + assert(is() && "Invalid accessor called"); + // If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3. + using Ty = typename ::wpi::PointerUnionTypeSelector< + PT1, T, IsInnerUnion, + ::wpi::PointerUnionTypeSelector>::Return; + return Ty(Val).template get(); + } + + /// Returns the current pointer if it is of the specified pointer type, + /// otherwises returns null. + template T dyn_cast() const { + if (is()) + return get(); + 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 +struct PointerLikeTypeTraits> { + static inline void *getAsVoidPointer(const PointerUnion3 &P) { + return P.getOpaqueValue(); + } + + static inline PointerUnion3 getFromVoidPointer(void *P) { + return PointerUnion3::getFromOpaqueValue(P); + } + + // The number of bits available are the min of the two pointer types. + enum { + NumLowBitsAvailable = PointerLikeTypeTraits< + typename PointerUnion3::ValTy>::NumLowBitsAvailable + }; +}; + +template +bool operator<(PointerUnion3 lhs, + PointerUnion3 rhs) { + return lhs.getOpaqueValue() < rhs.getOpaqueValue(); +} + +/// A pointer union of four pointer types. See documentation for PointerUnion +/// for usage. +template +class PointerUnion4 { +public: + using InnerUnion1 = PointerUnion; + using InnerUnion2 = PointerUnion; + using ValTy = PointerUnion; + +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 int is() const { + // If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2. + using Ty = typename ::wpi::PointerUnionTypeSelector< + PT1, T, InnerUnion1, + ::wpi::PointerUnionTypeSelector>::Return; + return Val.template is() && Val.template get().template is(); + } + + /// Returns the value of the specified pointer type. + /// + /// If the specified pointer type is incorrect, assert. + template T get() const { + assert(is() && "Invalid accessor called"); + // If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2. + using Ty = typename ::wpi::PointerUnionTypeSelector< + PT1, T, InnerUnion1, + ::wpi::PointerUnionTypeSelector>::Return; + return Val.template get().template get(); + } + + /// Returns the current pointer if it is of the specified pointer type, + /// otherwises returns null. + template T dyn_cast() const { + if (is()) + return get(); + 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 +struct PointerLikeTypeTraits> { + static inline void * + getAsVoidPointer(const PointerUnion4 &P) { + return P.getOpaqueValue(); + } + + static inline PointerUnion4 getFromVoidPointer(void *P) { + return PointerUnion4::getFromOpaqueValue(P); + } + + // The number of bits available are the min of the two pointer types. + enum { + NumLowBitsAvailable = PointerLikeTypeTraits< + typename PointerUnion4::ValTy>::NumLowBitsAvailable + }; +}; + +// Teach DenseMap how to use PointerUnions as keys. +template struct DenseMapInfo> { + using Pair = PointerUnion; + using FirstInfo = DenseMapInfo; + using SecondInfo = DenseMapInfo; + + 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::getHashValue(key); + } + + static bool isEqual(const Pair &LHS, const Pair &RHS) { + return LHS.template is() == RHS.template is() && + (LHS.template is() ? FirstInfo::isEqual(LHS.template get(), + RHS.template get()) + : SecondInfo::isEqual(LHS.template get(), + RHS.template get())); + } +}; + +} // end namespace wpi + +#endif // WPIUTIL_WPI_POINTERUNION_H diff --git a/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp new file mode 100644 index 0000000000..5dcd11fb87 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp @@ -0,0 +1,228 @@ +//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "wpi/FunctionExtras.h" +#include "gtest/gtest.h" + +#include + +using namespace wpi; + +namespace { + +TEST(UniqueFunctionTest, Basic) { + unique_function Sum = [](int A, int B) { return A + B; }; + EXPECT_EQ(Sum(1, 2), 3); + + unique_function Sum2 = std::move(Sum); + EXPECT_EQ(Sum2(1, 2), 3); + + unique_function Sum3 = [](int A, int B) { return A + B; }; + Sum2 = std::move(Sum3); + EXPECT_EQ(Sum2(1, 2), 3); + + Sum2 = unique_function([](int A, int B) { return A + B; }); + EXPECT_EQ(Sum2(1, 2), 3); + + // Explicit self-move test. + *&Sum2 = std::move(Sum2); + EXPECT_EQ(Sum2(1, 2), 3); + + Sum2 = unique_function(); + EXPECT_FALSE(Sum2); + + // Make sure we can forward through l-value reference parameters. + unique_function Inc = [](int &X) { ++X; }; + int X = 42; + Inc(X); + EXPECT_EQ(X, 43); + + // Make sure we can forward through r-value reference parameters with + // move-only types. + unique_function &&)> ReadAndDeallocByRef = + [](std::unique_ptr &&Ptr) { + int V = *Ptr; + Ptr.reset(); + return V; + }; + std::unique_ptr Ptr{new int(13)}; + EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13); + EXPECT_FALSE((bool)Ptr); + + // Make sure we can pass a move-only temporary as opposed to a local variable. + EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr(new int(42))), 42); + + // Make sure we can pass a move-only type by-value. + unique_function)> ReadAndDeallocByVal = + [](std::unique_ptr Ptr) { + int V = *Ptr; + Ptr.reset(); + return V; + }; + Ptr.reset(new int(13)); + EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13); + EXPECT_FALSE((bool)Ptr); + + EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr(new int(42))), 42); +} + +TEST(UniqueFunctionTest, Captures) { + long A = 1, B = 2, C = 3, D = 4, E = 5; + + unique_function Tmp; + + unique_function C1 = [A]() { return A; }; + EXPECT_EQ(C1(), 1); + Tmp = std::move(C1); + EXPECT_EQ(Tmp(), 1); + + unique_function C2 = [A, B]() { return A + B; }; + EXPECT_EQ(C2(), 3); + Tmp = std::move(C2); + EXPECT_EQ(Tmp(), 3); + + unique_function C3 = [A, B, C]() { return A + B + C; }; + EXPECT_EQ(C3(), 6); + Tmp = std::move(C3); + EXPECT_EQ(Tmp(), 6); + + unique_function C4 = [A, B, C, D]() { return A + B + C + D; }; + EXPECT_EQ(C4(), 10); + Tmp = std::move(C4); + EXPECT_EQ(Tmp(), 10); + + unique_function C5 = [A, B, C, D, E]() { return A + B + C + D + E; }; + EXPECT_EQ(C5(), 15); + Tmp = std::move(C5); + EXPECT_EQ(Tmp(), 15); +} + +TEST(UniqueFunctionTest, MoveOnly) { + struct SmallCallable { + std::unique_ptr A{new int(1)}; + + int operator()(int B) { return *A + B; } + }; + unique_function Small = SmallCallable(); + EXPECT_EQ(Small(2), 3); + unique_function Small2 = std::move(Small); + EXPECT_EQ(Small2(2), 3); + + struct LargeCallable { + std::unique_ptr A{new int(1)}; + std::unique_ptr B{new int(2)}; + std::unique_ptr C{new int(3)}; + std::unique_ptr D{new int(4)}; + std::unique_ptr E{new int(5)}; + + int operator()() { return *A + *B + *C + *D + *E; } + }; + unique_function Large = LargeCallable(); + EXPECT_EQ(Large(), 15); + unique_function Large2 = std::move(Large); + EXPECT_EQ(Large2(), 15); +} + +TEST(UniqueFunctionTest, CountForwardingCopies) { + struct CopyCounter { + int &CopyCount; + + CopyCounter(int &CopyCount) : CopyCount(CopyCount) {} + CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) { + ++CopyCount; + } + }; + + unique_function ByValF = [](CopyCounter) {}; + int CopyCount = 0; + ByValF(CopyCounter(CopyCount)); + EXPECT_EQ(1, CopyCount); + + CopyCount = 0; + { + CopyCounter Counter{CopyCount}; + ByValF(Counter); + } + EXPECT_EQ(2, CopyCount); + + // Check that we don't generate a copy at all when we can bind a reference all + // the way down, even if that reference could *in theory* allow copies. + unique_function ByRefF = [](const CopyCounter &) { + }; + CopyCount = 0; + ByRefF(CopyCounter(CopyCount)); + EXPECT_EQ(0, CopyCount); + + CopyCount = 0; + { + CopyCounter Counter{CopyCount}; + ByRefF(Counter); + } + EXPECT_EQ(0, CopyCount); + + // If we use a reference, we can make a stronger guarantee that *no* copy + // occurs. + struct Uncopyable { + Uncopyable() = default; + Uncopyable(const Uncopyable &) = delete; + }; + unique_function UncopyableF = + [](const Uncopyable &) {}; + UncopyableF(Uncopyable()); + Uncopyable X; + UncopyableF(X); +} + +TEST(UniqueFunctionTest, CountForwardingMoves) { + struct MoveCounter { + int &MoveCount; + + MoveCounter(int &MoveCount) : MoveCount(MoveCount) {} + MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; } + }; + + unique_function ByValF = [](MoveCounter) {}; + int MoveCount = 0; + ByValF(MoveCounter(MoveCount)); + EXPECT_EQ(1, MoveCount); + + MoveCount = 0; + { + MoveCounter Counter{MoveCount}; + ByValF(std::move(Counter)); + } + EXPECT_EQ(2, MoveCount); + + // Check that when we use an r-value reference we get no spurious copies. + unique_function ByRefF = [](MoveCounter &&) {}; + MoveCount = 0; + ByRefF(MoveCounter(MoveCount)); + EXPECT_EQ(0, MoveCount); + + MoveCount = 0; + { + MoveCounter Counter{MoveCount}; + ByRefF(std::move(Counter)); + } + EXPECT_EQ(0, MoveCount); + + // If we use an r-value reference we can in fact make a stronger guarantee + // with an unmovable type. + struct Unmovable { + Unmovable() = default; + Unmovable(Unmovable &&) = delete; + }; + unique_function UnmovableF = [](const Unmovable &) { + }; + UnmovableF(Unmovable()); + Unmovable X; + UnmovableF(X); +} + +} // anonymous namespace