[llvm-commits] [llvm] r155979 - in /llvm/trunk/include/llvm: ADT/SmallVector.h Support/Compiler.h

David Blaikie via llvm-commits llvm-commits at lists.llvm.org
Thu May 26 16:27:16 PDT 2016


/casts Necromancy

(Sorry for the duplicate, John - forgot to switch mailing lists, of course)

On Tue, May 1, 2012 at 10:39 PM, John McCall <rjmccall at apple.com> wrote:

> Author: rjmccall
> Date: Wed May  2 00:39:15 2012
> New Revision: 155979
>
> URL: http://llvm.org/viewvc/llvm-project?rev=155979&view=rev
> Log:
> Update SmallVector to support move semantics if the host does.
> Note that support for rvalue references does not imply support
> for the full set of move-related STL operations.
>
> I've preserved support for an odd little thing in insert() where
> we're trying to support inserting a new element from an existing
> one.  If we actually want to support that, there's a lot more we
> need to do:  insert can call either grow or push_back, neither of
> which is safe against this particular use pattern.
>
> Modified:
>     llvm/trunk/include/llvm/ADT/SmallVector.h
>     llvm/trunk/include/llvm/Support/Compiler.h
>
> Modified: llvm/trunk/include/llvm/ADT/SmallVector.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/SmallVector.h?rev=155979&r1=155978&r2=155979&view=diff
>
> ==============================================================================
> --- llvm/trunk/include/llvm/ADT/SmallVector.h (original)
> +++ llvm/trunk/include/llvm/ADT/SmallVector.h Wed May  2 00:39:15 2012
> @@ -14,6 +14,7 @@
>  #ifndef LLVM_ADT_SMALLVECTOR_H
>  #define LLVM_ADT_SMALLVECTOR_H
>
> +#include "llvm/Support/Compiler.h"
>  #include "llvm/Support/type_traits.h"
>  #include <algorithm>
>  #include <cassert>
> @@ -54,6 +55,11 @@
>      return BeginX == static_cast<const void*>(&FirstEl);
>    }
>
> +  /// resetToSmall - Put this vector in a state of being small.
> +  void resetToSmall() {
> +    BeginX = EndX = CapacityX = &FirstEl;
> +  }
> +
>    /// grow_pod - This is an implementation of the grow() method which
> only works
>    /// on POD-like data types and is out of line to reduce code
> duplication.
>    void grow_pod(size_t MinSizeInBytes, size_t TSize);
> @@ -160,28 +166,84 @@
>      }
>    }
>
> -  /// uninitialized_copy - Copy the range [I, E) onto the uninitialized
> memory
> -  /// starting with "Dest", constructing elements into it as needed.
> +  /// move - Use move-assignment to move the range [I, E) onto the
> +  /// objects starting with "Dest".  This is just <memory>'s
> +  /// std::move, but not all stdlibs actually provide that.
> +  template<typename It1, typename It2>
> +  static It2 move(It1 I, It1 E, It2 Dest) {
> +#if LLVM_USE_RVALUE_REFERENCES
> +    for (; I != E; ++I, ++Dest)
> +      *Dest = ::std::move(*I);
> +    return Dest;
> +#else
> +    return ::std::copy(I, E, Dest);
> +#endif
> +  }
> +
> +  /// move_backward - Use move-assignment to move the range
> +  /// [I, E) onto the objects ending at "Dest", moving objects
> +  /// in reverse order.  This is just <algorithm>'s
> +  /// std::move_backward, but not all stdlibs actually provide that.
> +  template<typename It1, typename It2>
> +  static It2 move_backward(It1 I, It1 E, It2 Dest) {
> +#if LLVM_USE_RVALUE_REFERENCES
> +    while (I != E)
> +      *--Dest = ::std::move(*--E);
> +    return Dest;
> +#else
> +    return ::std::copy_backward(I, E, Dest);
> +#endif
> +  }
> +
> +  /// uninitialized_move - Move the range [I, E) into the uninitialized
> +  /// memory starting with "Dest", constructing elements as needed.
> +  template<typename It1, typename It2>
> +  static void uninitialized_move(It1 I, It1 E, It2 Dest) {
> +#if LLVM_USE_RVALUE_REFERENCES
> +    for (; I != E; ++I, ++Dest)
> +      ::new ((void*) &*Dest) T(::std::move(*I));
>>
>> Hey John - I was just looking over all this code & was wondering why
>> these static move_backwards and move(iter, iter, iter) were implemented
>> this way.
>
>
>> Practically speaking, what I mean is: Can I remove them all and just
>> replace the call sites with std::move(iter, iter, iter) and
>> std::move_backward?
>
>
>> (It looks like in this original version the loops were written out
>> manually - perhaps because we were running on platforms that didn't have
>> those library functions implemented? Then we... hmm, nope, they're still
>> implemented explicitly in the isPodLike=false case - I assume we could just
>> move this all to std::move/std::move_backwards & we'd be good to go?)
>
> +#else
> +    ::std::uninitialized_copy(I, E, Dest);
> +#endif
> +  }
> +
> +  /// uninitialized_copy - Copy the range [I, E) onto the uninitialized
> +  /// memory starting with "Dest", constructing elements as needed.
>    template<typename It1, typename It2>
>    static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
>      std::uninitialized_copy(I, E, Dest);
>    }
>
> -  /// grow - double the size of the allocated memory, guaranteeing space
> for at
> -  /// least one more element or MinSize if specified.
> +  /// grow - Grow the allocated memory (without initializing new
> +  /// elements), doubling the size of the allocated memory.
> +  /// Guarantees space for at least one more element, or MinSize more
> +  /// elements if specified.
>    void grow(size_t MinSize = 0);
>
>  public:
>    void push_back(const T &Elt) {
>      if (this->EndX < this->CapacityX) {
>      Retry:
> -      new (this->end()) T(Elt);
> +      ::new ((void*) this->end()) T(Elt);
> +      this->setEnd(this->end()+1);
> +      return;
> +    }
> +    this->grow();
> +    goto Retry;
> +  }
> +
> +#if LLVM_USE_RVALUE_REFERENCES
> +  void push_back(T &&Elt) {
> +    if (this->EndX < this->CapacityX) {
> +    Retry:
> +      ::new ((void*) this->end()) T(::std::move(Elt));
>        this->setEnd(this->end()+1);
>        return;
>      }
>      this->grow();
>      goto Retry;
>    }
> +#endif
>
>    void pop_back() {
>      this->setEnd(this->end()-1);
> @@ -199,8 +261,8 @@
>      NewCapacity = MinSize;
>    T *NewElts = static_cast<T*>(malloc(NewCapacity*sizeof(T)));
>
> -  // Copy the elements over.
> -  this->uninitialized_copy(this->begin(), this->end(), NewElts);
> +  // Move the elements over.
> +  this->uninitialized_move(this->begin(), this->end(), NewElts);
>
>    // Destroy the original elements.
>    destroy_range(this->begin(), this->end());
> @@ -225,6 +287,29 @@
>    // No need to do a destroy loop for POD's.
>    static void destroy_range(T *, T *) {}
>
> +  /// move - Use move-assignment to move the range [I, E) onto the
> +  /// objects starting with "Dest".  For PODs, this is just memcpy.
> +  template<typename It1, typename It2>
> +  static It2 move(It1 I, It1 E, It2 Dest) {
> +    return ::std::copy(I, E, Dest);
>

Hey John - I was just looking over all this code & was wondering why these
static move_backwards and move(iter, iter, iter) were implemented this way.

Practically speaking, what I mean is: Can I remove them all and just
replace the call sites with std::move(iter, iter, iter) and
std::move_backward?

(It looks like in this original version the loops were written out manually
- perhaps because we were running on platforms that didn't have those
library functions implemented? Then we... hmm, nope, they're still
implemented explicitly in the isPodLike=false case - I assume we could just
move this all to std::move/std::move_backwards & we'd be good to go?)


> +  }
> +
> +  /// move_backward - Use move-assignment to move the range
> +  /// [I, E) onto the objects ending at "Dest", moving objects
> +  /// in reverse order.
> +  template<typename It1, typename It2>
> +  static It2 move_backward(It1 I, It1 E, It2 Dest) {
> +    return ::std::copy_backward(I, E, Dest);
> +  }
> +
> +  /// uninitialized_move - Move the range [I, E) onto the uninitialized
> memory
> +  /// starting with "Dest", constructing elements into it as needed.
> +  template<typename It1, typename It2>
> +  static void uninitialized_move(It1 I, It1 E, It2 Dest) {
> +    // Just do a copy.
> +    uninitialized_copy(I, E, Dest);
> +  }
> +
>    /// uninitialized_copy - Copy the range [I, E) onto the uninitialized
> memory
>    /// starting with "Dest", constructing elements into it as needed.
>    template<typename It1, typename It2>
> @@ -330,7 +415,11 @@
>    }
>
>    T pop_back_val() {
> +#if LLVM_USE_RVALUE_REFERENCES
> +    T Result = ::std::move(this->back());
> +#else
>      T Result = this->back();
> +#endif
>      this->pop_back();
>      return Result;
>    }
> @@ -392,6 +481,36 @@
>      return(N);
>    }
>
> +#if LLVM_USE_RVALUE_REFERENCES
> +  iterator insert(iterator I, T &&Elt) {
> +    if (I == this->end()) {  // Important special case for empty vector.
> +      this->push_back(::std::move(Elt));
> +      return this->end()-1;
> +    }
> +
> +    if (this->EndX < this->CapacityX) {
> +    Retry:
> +      ::new ((void*) this->end()) T(::std::move(this->back()));
> +      this->setEnd(this->end()+1);
> +      // Push everything else over.
> +      this->move_backward(I, this->end()-1, this->end());
> +
> +      // If we just moved the element we're inserting, be sure to update
> +      // the reference.
> +      T *EltPtr = &Elt;
> +      if (I <= EltPtr && EltPtr < this->EndX)
> +        ++EltPtr;
> +
> +      *I = ::std::move(*EltPtr);
> +      return I;
> +    }
> +    size_t EltNo = I-this->begin();
> +    this->grow();
> +    I = this->begin()+EltNo;
> +    goto Retry;
> +  }
> +#endif
> +
>    iterator insert(iterator I, const T &Elt) {
>      if (I == this->end()) {  // Important special case for empty vector.
>        this->push_back(Elt);
> @@ -400,10 +519,10 @@
>
>      if (this->EndX < this->CapacityX) {
>      Retry:
> -      new (this->end()) T(this->back());
> +      ::new ((void*) this->end()) T(this->back());
>        this->setEnd(this->end()+1);
>        // Push everything else over.
> -      std::copy_backward(I, this->end()-1, this->end());
> +      this->move_backward(I, this->end()-1, this->end());
>
>        // If we just moved the element we're inserting, be sure to update
>        // the reference.
> @@ -444,7 +563,7 @@
>        append(this->end()-NumToInsert, this->end());
>
>        // Copy the existing elements that get replaced.
> -      std::copy_backward(I, OldEnd-NumToInsert, OldEnd);
> +      this->move_backward(I, OldEnd-NumToInsert, OldEnd);
>
>        std::fill_n(I, NumToInsert, Elt);
>        return I;
> @@ -493,7 +612,7 @@
>        append(this->end()-NumToInsert, this->end());
>
>        // Copy the existing elements that get replaced.
> -      std::copy_backward(I, OldEnd-NumToInsert, OldEnd);
> +      this->move_backward(I, OldEnd-NumToInsert, OldEnd);
>
>        std::copy(From, To, I);
>        return I;
> @@ -519,8 +638,11 @@
>      return I;
>    }
>
> -  const SmallVectorImpl
> -  &operator=(const SmallVectorImpl &RHS);
> +  SmallVectorImpl &operator=(const SmallVectorImpl &RHS);
> +
> +#if LLVM_USE_RVALUE_REFERENCES
> +  SmallVectorImpl &operator=(SmallVectorImpl &&RHS);
> +#endif
>
>    bool operator==(const SmallVectorImpl &RHS) const {
>      if (this->size() != RHS.size()) return false;
> @@ -590,7 +712,7 @@
>  }
>
>  template <typename T>
> -const SmallVectorImpl<T> &SmallVectorImpl<T>::
> +SmallVectorImpl<T> &SmallVectorImpl<T>::
>    operator=(const SmallVectorImpl<T> &RHS) {
>    // Avoid self-assignment.
>    if (this == &RHS) return *this;
> @@ -617,6 +739,7 @@
>
>    // If we have to grow to have enough elements, destroy the current
> elements.
>    // This allows us to avoid copying them during the grow.
> +  // FIXME: don't do this if they're efficiently moveable.
>    if (this->capacity() < RHSSize) {
>      // Destroy current elements.
>      this->destroy_range(this->begin(), this->end());
> @@ -637,6 +760,69 @@
>    return *this;
>  }
>
> +#if LLVM_USE_RVALUE_REFERENCES
> +template <typename T>
> +SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T>
> &&RHS) {
> +  // Avoid self-assignment.
> +  if (this == &RHS) return *this;
> +
> +  // If the RHS isn't small, clear this vector and then steal its buffer.
> +  if (!RHS.isSmall()) {
> +    this->destroy_range(this->begin(), this->end());
> +    if (!this->isSmall()) free(this->begin());
> +    this->BeginX = RHS.BeginX;
> +    this->EndX = RHS.EndX;
> +    this->CapacityX = RHS.CapacityX;
> +    RHS.resetToSmall();
> +    return *this;
> +  }
> +
> +  // If we already have sufficient space, assign the common elements, then
> +  // destroy any excess.
> +  size_t RHSSize = RHS.size();
> +  size_t CurSize = this->size();
> +  if (CurSize >= RHSSize) {
> +    // Assign common elements.
> +    iterator NewEnd = this->begin();
> +    if (RHSSize)
> +      NewEnd = this->move(RHS.begin(), RHS.end(), NewEnd);
> +
> +    // Destroy excess elements and trim the bounds.
> +    this->destroy_range(NewEnd, this->end());
> +    this->setEnd(NewEnd);
> +
> +    // Clear the RHS.
> +    RHS.clear();
> +
> +    return *this;
> +  }
> +
> +  // If we have to grow to have enough elements, destroy the current
> elements.
> +  // This allows us to avoid copying them during the grow.
> +  // FIXME: this may not actually make any sense if we can efficiently
> move
> +  // elements.
> +  if (this->capacity() < RHSSize) {
> +    // Destroy current elements.
> +    this->destroy_range(this->begin(), this->end());
> +    this->setEnd(this->begin());
> +    CurSize = 0;
> +    this->grow(RHSSize);
> +  } else if (CurSize) {
> +    // Otherwise, use assignment for the already-constructed elements.
> +    this->move(RHS.begin(), RHS.end(), this->begin());
> +  }
> +
> +  // Move-construct the new elements in place.
> +  this->uninitialized_move(RHS.begin()+CurSize, RHS.end(),
> +                           this->begin()+CurSize);
> +
> +  // Set end.
> +  this->setEnd(this->begin()+RHSSize);
> +
> +  RHS.clear();
> +  return *this;
> +}
> +#endif
>
>  /// SmallVector - This is a 'vector' (really, a variable-sized array),
> optimized
>  /// for the case when the array is small.  It contains some number of
> elements
> @@ -692,6 +878,18 @@
>      return *this;
>    }
>
> +#if LLVM_USE_RVALUE_REFERENCES
> +  SmallVector(SmallVector &&RHS) : SmallVectorImpl<T>(NumTsAvailable) {
> +    if (!RHS.empty())
> +      SmallVectorImpl<T>::operator=(::std::move(RHS));
> +  }
> +
> +  const SmallVector &operator=(SmallVector &&RHS) {
> +    SmallVectorImpl<T>::operator=(::std::move(RHS));
> +    return *this;
> +  }
> +#endif
> +
>  };
>
>  /// Specialize SmallVector at N=0.  This specialization guarantees
>
> Modified: llvm/trunk/include/llvm/Support/Compiler.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Compiler.h?rev=155979&r1=155978&r2=155979&view=diff
>
> ==============================================================================
> --- llvm/trunk/include/llvm/Support/Compiler.h (original)
> +++ llvm/trunk/include/llvm/Support/Compiler.h Wed May  2 00:39:15 2012
> @@ -19,6 +19,25 @@
>  # define __has_feature(x) 0
>  #endif
>
> +/// LLVM_HAS_RVALUE_REFERENCES - Does the compiler provide r-value
> references?
> +/// This implies that <utility> provides the one-argument std::move;  it
> +/// does not imply the existence of any other C++ library features.
> +#if (__has_feature(cxx_rvalue_references)   \
> +     || defined(__GXX_EXPERIMENTAL_CXX0X__) \
> +     || _MSC_VER >= 1600)
> +#define LLVM_USE_RVALUE_REFERENCES 1
> +#else
> +#define LLVM_USE_RVALUE_REFERENCES 0
> +#endif
> +
> +/// llvm_move - Expands to ::std::move if the compiler supports
> +/// r-value references; otherwise, expands to the argument.
> +#if LLVM_USE_RVALUE_REFERENCES
> +#define llvm_move(value) (::std::move(arg))
> +#else
> +#define llvm_move(value) (value)
> +#endif
> +
>  /// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is
> linked
>  /// into a shared library, then the class should be private to the
> library and
>  /// not accessible from outside it.  Can also be used to mark variables
> and
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20160526/f33af426/attachment.html>


More information about the llvm-commits mailing list