<div dir="ltr">Sorry, yeah. There is a bit of thread w/ Eli about this. We need an explicit alignment attribute. I'll add one ASAP...</div><br><div class="gmail_quote"><div dir="ltr">On Tue, Jul 3, 2018 at 12:24 PM Aditya Nandakumar <<a href="mailto:aditya_nandakumar@apple.com">aditya_nandakumar@apple.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Chandler,<br>
<br>
This seems to be failing on MacOS (The UnitTest segfaults consistently on our bots)<br>
I see this<br>
<br>
Assertion failed: ((reinterpret_cast<uintptr_t>(P) & ~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 && “Alignment not satisfied for an actual function pointer!"), function getAsVoidPointer, file llvm/include/llvm/Support/PointerLikeTypeTraits.h, line 129<br>
<br>
Can you please take a look?<br>
<br>
> On Jul 2, 2018, at 4:57 PM, Chandler Carruth via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>> wrote:<br>
> <br>
> Author: chandlerc<br>
> Date: Mon Jul  2 16:57:29 2018<br>
> New Revision: 336156<br>
> <br>
> URL: <a href="http://llvm.org/viewvc/llvm-project?rev=336156&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=336156&view=rev</a><br>
> Log:<br>
> [ADT] Add llvm::unique_function which is like std::function but<br>
> supporting move-only closures.<br>
> <br>
> Most of the core optimizations for std::function are here plus<br>
> a potentially novel one that detects trivially movable and destroyable<br>
> functors and implements those with fewer indirections.<br>
> <br>
> This is especially useful as we start trying to add concurrency<br>
> primitives as those often end up with move-only types (futures,<br>
> promises, etc) and wanting them to work through lambdas.<br>
> <br>
> As further work, we could add better support for things like const-qualified<br>
> operator()s to support more algorithms, and r-value ref qualified operator()s<br>
> to model call-once. None of that is here though.<br>
> <br>
> We can also provide our own llvm::function that has some of the optimizations<br>
> used in this class, but with copy semantics instead of move semantics.<br>
> <br>
> This is motivated by increasing usage of things like executors and the task<br>
> queue where it is useful to embed move-only types like a std::promise within<br>
> a type erased function. That isn't possible without this version of a type<br>
> erased function.<br>
> <br>
> Differential Revision: <a href="https://reviews.llvm.org/D48349" rel="noreferrer" target="_blank">https://reviews.llvm.org/D48349</a><br>
> <br>
> Added:<br>
>    llvm/trunk/include/llvm/ADT/FunctionExtras.h<br>
>    llvm/trunk/unittests/ADT/FunctionExtrasTest.cpp<br>
> Modified:<br>
>    llvm/trunk/include/llvm/Support/Compiler.h<br>
>    llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h<br>
>    llvm/trunk/unittests/ADT/CMakeLists.txt<br>
> <br>
> Added: llvm/trunk/include/llvm/ADT/FunctionExtras.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/FunctionExtras.h?rev=336156&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/FunctionExtras.h?rev=336156&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/include/llvm/ADT/FunctionExtras.h (added)<br>
> +++ llvm/trunk/include/llvm/ADT/FunctionExtras.h Mon Jul  2 16:57:29 2018<br>
> @@ -0,0 +1,274 @@<br>
> +//===- FunctionExtras.h - Function type erasure utilities -------*- C++ -*-===//<br>
> +//<br>
> +//                     The LLVM Compiler Infrastructure<br>
> +//<br>
> +// This file is distributed under the University of Illinois Open Source<br>
> +// License. See LICENSE.TXT for details.<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +/// \file<br>
> +/// This file provides a collection of function (or more generally, callable)<br>
> +/// type erasure utilities supplementing those provided by the standard library<br>
> +/// in `<function>`.<br>
> +///<br>
> +/// It provides `unique_function`, which works like `std::function` but supports<br>
> +/// move-only callable objects.<br>
> +///<br>
> +/// Future plans:<br>
> +/// - Add a `function` that provides const, volatile, and ref-qualified support,<br>
> +///   which doesn't work with `std::function`.<br>
> +/// - Provide support for specifying multiple signatures to type erase callable<br>
> +///   objects with an overload set, such as those produced by generic lambdas.<br>
> +/// - Expand to include a copyable utility that directly replaces std::function<br>
> +///   but brings the above improvements.<br>
> +///<br>
> +/// Note that LLVM's utilities are greatly simplified by not supporting<br>
> +/// allocators.<br>
> +///<br>
> +/// If the standard library ever begins to provide comparable facilities we can<br>
> +/// consider switching to those.<br>
> +///<br>
> +//===----------------------------------------------------------------------===//<br>
> +<br>
> +#ifndef LLVM_ADT_FUNCTION_EXTRAS_H<br>
> +#define LLVM_ADT_FUNCTION_EXTRAS_H<br>
> +<br>
> +#include "llvm/ADT/PointerIntPair.h"<br>
> +#include "llvm/ADT/PointerUnion.h"<br>
> +#include <memory><br>
> +#include <type_traits><br>
> +<br>
> +namespace llvm {<br>
> +<br>
> +template <typename FunctionT> class unique_function;<br>
> +<br>
> +template <typename ReturnT, typename... ParamTs><br>
> +class unique_function<ReturnT(ParamTs...)> {<br>
> +  static constexpr int InlineStorageSize = sizeof(void *) * 3;<br>
> +<br>
> +  // Provide a type function to map parameters that won't observe extra copies<br>
> +  // or moves and which are small enough to likely pass in register to values<br>
> +  // and all other types to l-value reference types. We use this to compute the<br>
> +  // types used in our erased call utility to minimize copies and moves unless<br>
> +  // doing so would force things unnecessarily into memory.<br>
> +  //<br>
> +  // The heuristic used is related to common ABI register passing conventions.<br>
> +  // It doesn't have to be exact though, and in one way it is more strict<br>
> +  // because we want to still be able to observe either moves *or* copies.<br>
> +  template <typename T><br>
> +  using AdjustedParamT = typename std::conditional<<br>
> +      !std::is_reference<T>::value &&<br>
> +          std::is_trivially_copy_constructible<T>::value &&<br>
> +          std::is_trivially_move_constructible<T>::value &&<br>
> +          sizeof(T) <= (2 * sizeof(void *)),<br>
> +      T, T &>::type;<br>
> +<br>
> +  // The type of the erased function pointer we use as a callback to dispatch to<br>
> +  // the stored callable when it is trivial to move and destroy.<br>
> +  using CallPtrT = ReturnT (*)(void *CallableAddr,<br>
> +                               AdjustedParamT<ParamTs>... Params);<br>
> +  using MovePtrT = void (*)(void *LHSCallableAddr, void *RHSCallableAddr);<br>
> +  using DestroyPtrT = void (*)(void *CallableAddr);<br>
> +<br>
> +  /// A struct we use to aggregate three callbacks when we need full set of<br>
> +  /// operations.<br>
> +  struct NonTrivialCallbacks {<br>
> +    CallPtrT CallPtr;<br>
> +    MovePtrT MovePtr;<br>
> +    DestroyPtrT DestroyPtr;<br>
> +  };<br>
> +<br>
> +  // Now we can create a pointer union between either a direct, trivial call<br>
> +  // pointer and a pointer to a static struct of the call, move, and destroy<br>
> +  // pointers. We do this to keep the footprint in this object a single pointer<br>
> +  // while supporting all the necessary type-erased operation.<br>
> +  using CallbackPointerUnionT = PointerUnion<CallPtrT, NonTrivialCallbacks *>;<br>
> +<br>
> +  // The main storage buffer. This will either have a pointer to out-of-line<br>
> +  // storage or an inline buffer storing the callable.<br>
> +  union StorageUnionT {<br>
> +    // For out-of-line storage we keep a pointer to the underlying storage and<br>
> +    // the size. This is enough to deallocate the memory.<br>
> +    struct OutOfLineStorageT {<br>
> +      void *StoragePtr;<br>
> +      size_t Size;<br>
> +      size_t Alignment;<br>
> +    } OutOfLineStorage;<br>
> +    static_assert(<br>
> +        sizeof(OutOfLineStorageT) <= InlineStorageSize,<br>
> +        "Should always use all of the out-of-line storage for inline storage!");<br>
> +<br>
> +    // For in-line storage, we just provide an aligned character buffer. We<br>
> +    // provide three pointers worth of storage here.<br>
> +    typename std::aligned_storage<InlineStorageSize, alignof(void *)>::type<br>
> +        InlineStorage;<br>
> +  } StorageUnion;<br>
> +<br>
> +  // A compressed pointer to either our dispatching callback or our table of<br>
> +  // dispatching callbacks and the flag for whether the callable itself is<br>
> +  // stored inline or not.<br>
> +  PointerIntPair<CallbackPointerUnionT, 1, bool> CallbackAndInlineFlag;<br>
> +<br>
> +  bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); }<br>
> +<br>
> +  bool isTrivialCallback() const {<br>
> +    return CallbackAndInlineFlag.getPointer().template is<CallPtrT>();<br>
> +  }<br>
> +<br>
> +  CallPtrT getTrivialCallback() const {<br>
> +    return CallbackAndInlineFlag.getPointer().template get<CallPtrT>();<br>
> +  }<br>
> +<br>
> +  NonTrivialCallbacks *getNonTrivialCallbacks() const {<br>
> +    return CallbackAndInlineFlag.getPointer()<br>
> +        .template get<NonTrivialCallbacks *>();<br>
> +  }<br>
> +<br>
> +  void *getInlineStorage() { return &StorageUnion.InlineStorage; }<br>
> +<br>
> +  void *getOutOfLineStorage() {<br>
> +    return StorageUnion.OutOfLineStorage.StoragePtr;<br>
> +  }<br>
> +  size_t getOutOfLineStorageSize() const {<br>
> +    return StorageUnion.OutOfLineStorage.Size;<br>
> +  }<br>
> +  size_t getOutOfLineStorageAlignment() const {<br>
> +    return StorageUnion.OutOfLineStorage.Alignment;<br>
> +  }<br>
> +<br>
> +  void setOutOfLineStorage(void *Ptr, size_t Size, size_t Alignment) {<br>
> +    StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment};<br>
> +  }<br>
> +<br>
> +  template <typename CallableT><br>
> +  static ReturnT CallImpl(void *CallableAddr,<br>
> +                          AdjustedParamT<ParamTs>... Params) {<br>
> +    return (*reinterpret_cast<CallableT *>(CallableAddr))(<br>
> +        std::forward<ParamTs>(Params)...);<br>
> +  }<br>
> +<br>
> +  template <typename CallableT><br>
> +  static void MoveImpl(void *LHSCallableAddr, void *RHSCallableAddr) noexcept {<br>
> +    new (LHSCallableAddr)<br>
> +        CallableT(std::move(*reinterpret_cast<CallableT *>(RHSCallableAddr)));<br>
> +  }<br>
> +<br>
> +  template <typename CallableT><br>
> +  static void DestroyImpl(void *CallableAddr) noexcept {<br>
> +    reinterpret_cast<CallableT *>(CallableAddr)->~CallableT();<br>
> +  }<br>
> +<br>
> +public:<br>
> +  unique_function() = default;<br>
> +  unique_function(std::nullptr_t /*null_callable*/) {}<br>
> +<br>
> +  ~unique_function() {<br>
> +    if (!CallbackAndInlineFlag.getPointer())<br>
> +      return;<br>
> +<br>
> +    // Cache this value so we don't re-check it after type-erased operations.<br>
> +    bool IsInlineStorage = isInlineStorage();<br>
> +<br>
> +    if (!isTrivialCallback())<br>
> +      getNonTrivialCallbacks()->DestroyPtr(<br>
> +          IsInlineStorage ? getInlineStorage() : getOutOfLineStorage());<br>
> +<br>
> +    if (!IsInlineStorage)<br>
> +      deallocate_buffer(getOutOfLineStorage(), getOutOfLineStorageSize(),<br>
> +                        getOutOfLineStorageAlignment());<br>
> +  }<br>
> +<br>
> +  unique_function(unique_function &&RHS) noexcept {<br>
> +    // Copy the callback and inline flag.<br>
> +    CallbackAndInlineFlag = RHS.CallbackAndInlineFlag;<br>
> +<br>
> +    // If the RHS is empty, just copying the above is sufficient.<br>
> +    if (!RHS)<br>
> +      return;<br>
> +<br>
> +    if (!isInlineStorage()) {<br>
> +      // The out-of-line case is easiest to move.<br>
> +      StorageUnion.OutOfLineStorage = RHS.StorageUnion.OutOfLineStorage;<br>
> +    } else if (isTrivialCallback()) {<br>
> +      // Move is trivial, just memcpy the bytes across.<br>
> +      memcpy(getInlineStorage(), RHS.getInlineStorage(), InlineStorageSize);<br>
> +    } else {<br>
> +      // Non-trivial move, so dispatch to a type-erased implementation.<br>
> +      getNonTrivialCallbacks()->MovePtr(getInlineStorage(),<br>
> +                                        RHS.getInlineStorage());<br>
> +    }<br>
> +<br>
> +    // Clear the old callback and inline flag to get back to as-if-null.<br>
> +    RHS.CallbackAndInlineFlag = {};<br>
> +<br>
> +#ifndef NDEBUG<br>
> +    // In debug builds, we also scribble across the rest of the storage.<br>
> +    memset(RHS.getInlineStorage(), 0xAD, InlineStorageSize);<br>
> +#endif<br>
> +  }<br>
> +<br>
> +  unique_function &operator=(unique_function &&RHS) noexcept {<br>
> +    if (this == &RHS)<br>
> +      return *this;<br>
> +<br>
> +    // Because we don't try to provide any exception safety guarantees we can<br>
> +    // implement move assignment very simply by first destroying the current<br>
> +    // object and then move-constructing over top of it.<br>
> +    this->~unique_function();<br>
> +    new (this) unique_function(std::move(RHS));<br>
> +    return *this;<br>
> +  }<br>
> +<br>
> +  template <typename CallableT> unique_function(CallableT Callable) {<br>
> +    bool IsInlineStorage = true;<br>
> +    void *CallableAddr = getInlineStorage();<br>
> +    if (sizeof(CallableT) > InlineStorageSize ||<br>
> +        alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) {<br>
> +      IsInlineStorage = false;<br>
> +      // Allocate out-of-line storage. FIXME: Use an explicit alignment<br>
> +      // parameter in C++17 mode.<br>
> +      auto Size = sizeof(CallableT);<br>
> +      auto Alignment = alignof(CallableT);<br>
> +      CallableAddr = allocate_buffer(Size, Alignment);<br>
> +      setOutOfLineStorage(CallableAddr, Size, Alignment);<br>
> +    }<br>
> +<br>
> +    // Now move into the storage.<br>
> +    new (CallableAddr) CallableT(std::move(Callable));<br>
> +<br>
> +    // See if we can create a trivial callback.<br>
> +    // FIXME: we should use constexpr if here and below to avoid instantiating<br>
> +    // the non-trivial static objects when unnecessary. While the linker should<br>
> +    // remove them, it is still wasteful.<br>
> +    if (std::is_trivially_move_constructible<CallableT>::value &&<br>
> +        std::is_trivially_destructible<CallableT>::value) {<br>
> +      CallbackAndInlineFlag = {&CallImpl<CallableT>, IsInlineStorage};<br>
> +      return;<br>
> +    }<br>
> +<br>
> +    // Otherwise, we need to point at an object with a vtable that contains all<br>
> +    // the different type erased behaviors needed. Create a static instance of<br>
> +    // the derived type here and then use a pointer to that.<br>
> +    static NonTrivialCallbacks Callbacks = {<br>
> +        &CallImpl<CallableT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};<br>
> +<br>
> +    CallbackAndInlineFlag = {&Callbacks, IsInlineStorage};<br>
> +  }<br>
> +<br>
> +  ReturnT operator()(ParamTs... Params) {<br>
> +    void *CallableAddr =<br>
> +        isInlineStorage() ? getInlineStorage() : getOutOfLineStorage();<br>
> +<br>
> +    return (isTrivialCallback()<br>
> +                ? getTrivialCallback()<br>
> +                : getNonTrivialCallbacks()->CallPtr)(CallableAddr, Params...);<br>
> +  }<br>
> +<br>
> +  explicit operator bool() const {<br>
> +    return (bool)CallbackAndInlineFlag.getPointer();<br>
> +  }<br>
> +};<br>
> +<br>
> +} // end namespace llvm<br>
> +<br>
> +#endif // LLVM_ADT_FUNCTION_H<br>
> <br>
> Modified: llvm/trunk/include/llvm/Support/Compiler.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Compiler.h?rev=336156&r1=336155&r2=336156&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Compiler.h?rev=336156&r1=336155&r2=336156&view=diff</a><br>
> ==============================================================================<br>
> --- llvm/trunk/include/llvm/Support/Compiler.h (original)<br>
> +++ llvm/trunk/include/llvm/Support/Compiler.h Mon Jul  2 16:57:29 2018<br>
> @@ -17,6 +17,9 @@<br>
> <br>
> #include "llvm/Config/llvm-config.h"<br>
> <br>
> +#include <new><br>
> +#include <stddef.h><br>
> +<br>
> #if defined(_MSC_VER)<br>
> #include <sal.h><br>
> #endif<br>
> @@ -503,4 +506,46 @@ void AnnotateIgnoreWritesEnd(const char<br>
> #define LLVM_ENABLE_EXCEPTIONS 1<br>
> #endif<br>
> <br>
> +namespace llvm {<br>
> +<br>
> +/// Allocate a buffer of memory with the given size and alignment.<br>
> +///<br>
> +/// When the compiler supports aligned operator new, this will use it to to<br>
> +/// handle even over-aligned allocations.<br>
> +///<br>
> +/// However, this doesn't make any attempt to leverage the fancier techniques<br>
> +/// like posix_memalign due to portability. It is mostly intended to allow<br>
> +/// compatibility with platforms that, after aligned allocation was added, use<br>
> +/// reduced default alignment.<br>
> +inline void *allocate_buffer(size_t Size, size_t Alignment) {<br>
> +  return ::operator new(Size<br>
> +#if __cpp_aligned_new<br>
> +                        ,<br>
> +                        std::align_val_t(Alignment)<br>
> +#endif<br>
> +  );<br>
> +}<br>
> +<br>
> +/// Deallocate a buffer of memory with the given size and alignment.<br>
> +///<br>
> +/// If supported, this will used the sized delete operator. Also if supported,<br>
> +/// this will pass the alignment to the delete operator.<br>
> +///<br>
> +/// The pointer must have been allocated with the corresponding new operator,<br>
> +/// most likely using the above helper.<br>
> +inline void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) {<br>
> +  ::operator delete(Ptr<br>
> +#if __cpp_sized_deallocation<br>
> +                    ,<br>
> +                    Size<br>
> +#endif<br>
> +#if __cpp_aligned_new<br>
> +                    ,<br>
> +                    std::align_val_t(Alignment)<br>
> +#endif<br>
> +  );<br>
> +}<br>
> +<br>
> +} // End namespace llvm<br>
> +<br>
> #endif<br>
> <br>
> Modified: llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h?rev=336156&r1=336155&r2=336156&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h?rev=336156&r1=336155&r2=336156&view=diff</a><br>
> ==============================================================================<br>
> --- llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h (original)<br>
> +++ llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h Mon Jul  2 16:57:29 2018<br>
> @@ -16,6 +16,7 @@<br>
> #define LLVM_SUPPORT_POINTERLIKETYPETRAITS_H<br>
> <br>
> #include "llvm/Support/DataTypes.h"<br>
> +#include <assert.h><br>
> #include <type_traits><br>
> <br>
> namespace llvm {<br>
> @@ -111,6 +112,39 @@ template <> struct PointerLikeTypeTraits<br>
>   enum { NumLowBitsAvailable = 0 };<br>
> };<br>
> <br>
> +/// Provide suitable custom traits struct for function pointers.<br>
> +///<br>
> +/// Function pointers can't be directly given these traits as functions can't<br>
> +/// have their alignment computed with `alignof` and we need different casting.<br>
> +///<br>
> +/// To rely on higher alignment for a specialized use, you can provide a<br>
> +/// customized form of this template explicitly with higher alignment, and<br>
> +/// potentially use alignment attributes on functions to satisfy that.<br>
> +template <int Alignment, typename FunctionPointerT><br>
> +struct FunctionPointerLikeTypeTraits {<br>
> +  enum { NumLowBitsAvailable = detail::ConstantLog2<Alignment>::value };<br>
> +  static inline void *getAsVoidPointer(FunctionPointerT P) {<br>
> +    assert((reinterpret_cast<uintptr_t>(P) &<br>
> +            ~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 &&<br>
> +           "Alignment not satisfied for an actual function pointer!");<br>
> +    return reinterpret_cast<void *>(P);<br>
> +  }<br>
> +  static inline FunctionPointerT getFromVoidPointer(void *P) {<br>
> +    return reinterpret_cast<FunctionPointerT>(P);<br>
> +  }<br>
> +};<br>
> +<br>
> +/// Provide a default specialization for function pointers that assumes 4-byte<br>
> +/// alignment.<br>
> +///<br>
> +/// We assume here that functions used with this are always at least 4-byte<br>
> +/// aligned. This means that, for example, thumb functions won't work or systems<br>
> +/// with weird unaligned function pointers won't work. But all practical systems<br>
> +/// we support satisfy this requirement.<br>
> +template <typename ReturnT, typename... ParamTs><br>
> +struct PointerLikeTypeTraits<ReturnT (*)(ParamTs...)><br>
> +    : FunctionPointerLikeTypeTraits<4, ReturnT (*)(ParamTs...)> {};<br>
> +<br>
> } // end namespace llvm<br>
> <br>
> #endif<br>
> <br>
> Modified: llvm/trunk/unittests/ADT/CMakeLists.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/CMakeLists.txt?rev=336156&r1=336155&r2=336156&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/CMakeLists.txt?rev=336156&r1=336155&r2=336156&view=diff</a><br>
> ==============================================================================<br>
> --- llvm/trunk/unittests/ADT/CMakeLists.txt (original)<br>
> +++ llvm/trunk/unittests/ADT/CMakeLists.txt Mon Jul  2 16:57:29 2018<br>
> @@ -18,6 +18,7 @@ add_llvm_unittest(ADTTests<br>
>   DepthFirstIteratorTest.cpp<br>
>   EquivalenceClassesTest.cpp<br>
>   FoldingSet.cpp<br>
> +  FunctionExtrasTest.cpp<br>
>   FunctionRefTest.cpp<br>
>   HashingTest.cpp<br>
>   IListBaseTest.cpp<br>
> <br>
> Added: llvm/trunk/unittests/ADT/FunctionExtrasTest.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/FunctionExtrasTest.cpp?rev=336156&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/FunctionExtrasTest.cpp?rev=336156&view=auto</a><br>
> ==============================================================================<br>
> --- llvm/trunk/unittests/ADT/FunctionExtrasTest.cpp (added)<br>
> +++ llvm/trunk/unittests/ADT/FunctionExtrasTest.cpp Mon Jul  2 16:57:29 2018<br>
> @@ -0,0 +1,228 @@<br>
> +//===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===//<br>
> +//<br>
> +//                     The LLVM Compiler Infrastructure<br>
> +//<br>
> +// This file is distributed under the University of Illinois Open Source<br>
> +// License. See LICENSE.TXT for details.<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +<br>
> +#include "llvm/ADT/FunctionExtras.h"<br>
> +#include "gtest/gtest.h"<br>
> +<br>
> +#include <memory><br>
> +<br>
> +using namespace llvm;<br>
> +<br>
> +namespace {<br>
> +<br>
> +TEST(UniqueFunctionTest, Basic) {<br>
> +  unique_function<int(int, int)> Sum = [](int A, int B) { return A + B; };<br>
> +  EXPECT_EQ(Sum(1, 2), 3);<br>
> +<br>
> +  unique_function<int(int, int)> Sum2 = std::move(Sum);<br>
> +  EXPECT_EQ(Sum2(1, 2), 3);<br>
> +<br>
> +  unique_function<int(int, int)> Sum3 = [](int A, int B) { return A + B; };<br>
> +  Sum2 = std::move(Sum3);<br>
> +  EXPECT_EQ(Sum2(1, 2), 3);<br>
> +<br>
> +  Sum2 = unique_function<int(int, int)>([](int A, int B) { return A + B; });<br>
> +  EXPECT_EQ(Sum2(1, 2), 3);<br>
> +<br>
> +  // Explicit self-move test.<br>
> +  *&Sum2 = std::move(Sum2);<br>
> +  EXPECT_EQ(Sum2(1, 2), 3);<br>
> +<br>
> +  Sum2 = unique_function<int(int, int)>();<br>
> +  EXPECT_FALSE(Sum2);<br>
> +<br>
> +  // Make sure we can forward through l-value reference parameters.<br>
> +  unique_function<void(int &)> Inc = [](int &X) { ++X; };<br>
> +  int X = 42;<br>
> +  Inc(X);<br>
> +  EXPECT_EQ(X, 43);<br>
> +<br>
> +  // Make sure we can forward through r-value reference parameters with<br>
> +  // move-only types.<br>
> +  unique_function<int(std::unique_ptr<int> &&)> ReadAndDeallocByRef =<br>
> +      [](std::unique_ptr<int> &&Ptr) {<br>
> +        int V = *Ptr;<br>
> +        Ptr.reset();<br>
> +        return V;<br>
> +      };<br>
> +  std::unique_ptr<int> Ptr{new int(13)};<br>
> +  EXPECT_EQ(ReadAndDeallocByRef(std::move(Ptr)), 13);<br>
> +  EXPECT_FALSE((bool)Ptr);<br>
> +<br>
> +  // Make sure we can pass a move-only temporary as opposed to a local variable.<br>
> +  EXPECT_EQ(ReadAndDeallocByRef(std::unique_ptr<int>(new int(42))), 42);<br>
> +<br>
> +  // Make sure we can pass a move-only type by-value.<br>
> +  unique_function<int(std::unique_ptr<int>)> ReadAndDeallocByVal =<br>
> +      [](std::unique_ptr<int> Ptr) {<br>
> +        int V = *Ptr;<br>
> +        Ptr.reset();<br>
> +        return V;<br>
> +      };<br>
> +  Ptr.reset(new int(13));<br>
> +  EXPECT_EQ(ReadAndDeallocByVal(std::move(Ptr)), 13);<br>
> +  EXPECT_FALSE((bool)Ptr);<br>
> +<br>
> +  EXPECT_EQ(ReadAndDeallocByVal(std::unique_ptr<int>(new int(42))), 42);<br>
> +}<br>
> +<br>
> +TEST(UniqueFunctionTest, Captures) {<br>
> +  long A = 1, B = 2, C = 3, D = 4, E = 5;<br>
> +<br>
> +  unique_function<long()> Tmp;<br>
> +<br>
> +  unique_function<long()> C1 = [A]() { return A; };<br>
> +  EXPECT_EQ(C1(), 1);<br>
> +  Tmp = std::move(C1);<br>
> +  EXPECT_EQ(Tmp(), 1);<br>
> +<br>
> +  unique_function<long()> C2 = [A, B]() { return A + B; };<br>
> +  EXPECT_EQ(C2(), 3);<br>
> +  Tmp = std::move(C2);<br>
> +  EXPECT_EQ(Tmp(), 3);<br>
> +<br>
> +  unique_function<long()> C3 = [A, B, C]() { return A + B + C; };<br>
> +  EXPECT_EQ(C3(), 6);<br>
> +  Tmp = std::move(C3);<br>
> +  EXPECT_EQ(Tmp(), 6);<br>
> +<br>
> +  unique_function<long()> C4 = [A, B, C, D]() { return A + B + C + D; };<br>
> +  EXPECT_EQ(C4(), 10);<br>
> +  Tmp = std::move(C4);<br>
> +  EXPECT_EQ(Tmp(), 10);<br>
> +<br>
> +  unique_function<long()> C5 = [A, B, C, D, E]() { return A + B + C + D + E; };<br>
> +  EXPECT_EQ(C5(), 15);<br>
> +  Tmp = std::move(C5);<br>
> +  EXPECT_EQ(Tmp(), 15);<br>
> +}<br>
> +<br>
> +TEST(UniqueFunctionTest, MoveOnly) {<br>
> +  struct SmallCallable {<br>
> +    std::unique_ptr<int> A{new int(1)};<br>
> +<br>
> +    int operator()(int B) { return *A + B; }<br>
> +  };<br>
> +  unique_function<int(int)> Small = SmallCallable();<br>
> +  EXPECT_EQ(Small(2), 3);<br>
> +  unique_function<int(int)> Small2 = std::move(Small);<br>
> +  EXPECT_EQ(Small2(2), 3);<br>
> +<br>
> +  struct LargeCallable {<br>
> +    std::unique_ptr<int> A{new int(1)};<br>
> +    std::unique_ptr<int> B{new int(2)};<br>
> +    std::unique_ptr<int> C{new int(3)};<br>
> +    std::unique_ptr<int> D{new int(4)};<br>
> +    std::unique_ptr<int> E{new int(5)};<br>
> +<br>
> +    int operator()() { return *A + *B + *C + *D + *E; }<br>
> +  };<br>
> +  unique_function<int()> Large = LargeCallable();<br>
> +  EXPECT_EQ(Large(), 15);<br>
> +  unique_function<int()> Large2 = std::move(Large);<br>
> +  EXPECT_EQ(Large2(), 15);<br>
> +}<br>
> +<br>
> +TEST(UniqueFunctionTest, CountForwardingCopies) {<br>
> +  struct CopyCounter {<br>
> +    int &CopyCount;<br>
> +<br>
> +    CopyCounter(int &CopyCount) : CopyCount(CopyCount) {}<br>
> +    CopyCounter(const CopyCounter &Arg) : CopyCount(Arg.CopyCount) {<br>
> +      ++CopyCount;<br>
> +    }<br>
> +  };<br>
> +<br>
> +  unique_function<void(CopyCounter)> ByValF = [](CopyCounter) {};<br>
> +  int CopyCount = 0;<br>
> +  ByValF(CopyCounter(CopyCount));<br>
> +  EXPECT_EQ(1, CopyCount);<br>
> +<br>
> +  CopyCount = 0;<br>
> +  {<br>
> +    CopyCounter Counter{CopyCount};<br>
> +    ByValF(Counter);<br>
> +  }<br>
> +  EXPECT_EQ(2, CopyCount);<br>
> +<br>
> +  // Check that we don't generate a copy at all when we can bind a reference all<br>
> +  // the way down, even if that reference could *in theory* allow copies.<br>
> +  unique_function<void(const CopyCounter &)> ByRefF = [](const CopyCounter &) {<br>
> +  };<br>
> +  CopyCount = 0;<br>
> +  ByRefF(CopyCounter(CopyCount));<br>
> +  EXPECT_EQ(0, CopyCount);<br>
> +<br>
> +  CopyCount = 0;<br>
> +  {<br>
> +    CopyCounter Counter{CopyCount};<br>
> +    ByRefF(Counter);<br>
> +  }<br>
> +  EXPECT_EQ(0, CopyCount);<br>
> +<br>
> +  // If we use a reference, we can make a stronger guarantee that *no* copy<br>
> +  // occurs.<br>
> +  struct Uncopyable {<br>
> +    Uncopyable() = default;<br>
> +    Uncopyable(const Uncopyable &) = delete;<br>
> +  };<br>
> +  unique_function<void(const Uncopyable &)> UncopyableF =<br>
> +      [](const Uncopyable &) {};<br>
> +  UncopyableF(Uncopyable());<br>
> +  Uncopyable X;<br>
> +  UncopyableF(X);<br>
> +}<br>
> +<br>
> +TEST(UniqueFunctionTest, CountForwardingMoves) {<br>
> +  struct MoveCounter {<br>
> +    int &MoveCount;<br>
> +<br>
> +    MoveCounter(int &MoveCount) : MoveCount(MoveCount) {}<br>
> +    MoveCounter(MoveCounter &&Arg) : MoveCount(Arg.MoveCount) { ++MoveCount; }<br>
> +  };<br>
> +<br>
> +  unique_function<void(MoveCounter)> ByValF = [](MoveCounter) {};<br>
> +  int MoveCount = 0;<br>
> +  ByValF(MoveCounter(MoveCount));<br>
> +  EXPECT_EQ(1, MoveCount);<br>
> +<br>
> +  MoveCount = 0;<br>
> +  {<br>
> +    MoveCounter Counter{MoveCount};<br>
> +    ByValF(std::move(Counter));<br>
> +  }<br>
> +  EXPECT_EQ(2, MoveCount);<br>
> +<br>
> +  // Check that when we use an r-value reference we get no spurious copies.<br>
> +  unique_function<void(MoveCounter &&)> ByRefF = [](MoveCounter &&) {};<br>
> +  MoveCount = 0;<br>
> +  ByRefF(MoveCounter(MoveCount));<br>
> +  EXPECT_EQ(0, MoveCount);<br>
> +<br>
> +  MoveCount = 0;<br>
> +  {<br>
> +    MoveCounter Counter{MoveCount};<br>
> +    ByRefF(std::move(Counter));<br>
> +  }<br>
> +  EXPECT_EQ(0, MoveCount);<br>
> +<br>
> +  // If we use an r-value reference we can in fact make a stronger guarantee<br>
> +  // with an unmovable type.<br>
> +  struct Unmovable {<br>
> +    Unmovable() = default;<br>
> +    Unmovable(Unmovable &&) = delete;<br>
> +  };<br>
> +  unique_function<void(const Unmovable &)> UnmovableF = [](const Unmovable &) {<br>
> +  };<br>
> +  UnmovableF(Unmovable());<br>
> +  Unmovable X;<br>
> +  UnmovableF(X);<br>
> +}<br>
> +<br>
> +} // anonymous namespace<br>
> <br>
> <br>
> _______________________________________________<br>
> llvm-commits mailing list<br>
> <a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
> <a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
<br>
</blockquote></div>