<div dir="ltr">/casts Necromancy<br><br>(Sorry for the duplicate, John - forgot to switch mailing lists, of course)<br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, May 1, 2012 at 10:39 PM, John McCall <span dir="ltr"><<a href="mailto:rjmccall@apple.com" target="_blank">rjmccall@apple.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Author: rjmccall<br>
Date: Wed May  2 00:39:15 2012<br>
New Revision: 155979<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=155979&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=155979&view=rev</a><br>
Log:<br>
Update SmallVector to support move semantics if the host does.<br>
Note that support for rvalue references does not imply support<br>
for the full set of move-related STL operations.<br>
<br>
I've preserved support for an odd little thing in insert() where<br>
we're trying to support inserting a new element from an existing<br>
one.  If we actually want to support that, there's a lot more we<br>
need to do:  insert can call either grow or push_back, neither of<br>
which is safe against this particular use pattern.<br>
<br>
Modified:<br>
    llvm/trunk/include/llvm/ADT/SmallVector.h<br>
    llvm/trunk/include/llvm/Support/Compiler.h<br>
<br>
Modified: llvm/trunk/include/llvm/ADT/SmallVector.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/SmallVector.h?rev=155979&r1=155978&r2=155979&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/SmallVector.h?rev=155979&r1=155978&r2=155979&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/ADT/SmallVector.h (original)<br>
+++ llvm/trunk/include/llvm/ADT/SmallVector.h Wed May  2 00:39:15 2012<br>
@@ -14,6 +14,7 @@<br>
 #ifndef LLVM_ADT_SMALLVECTOR_H<br>
 #define LLVM_ADT_SMALLVECTOR_H<br>
<br>
+#include "llvm/Support/Compiler.h"<br>
 #include "llvm/Support/type_traits.h"<br>
 #include <algorithm><br>
 #include <cassert><br>
@@ -54,6 +55,11 @@<br>
     return BeginX == static_cast<const void*>(&FirstEl);<br>
   }<br>
<br>
+  /// resetToSmall - Put this vector in a state of being small.<br>
+  void resetToSmall() {<br>
+    BeginX = EndX = CapacityX = &FirstEl;<br>
+  }<br>
+<br>
   /// grow_pod - This is an implementation of the grow() method which only works<br>
   /// on POD-like data types and is out of line to reduce code duplication.<br>
   void grow_pod(size_t MinSizeInBytes, size_t TSize);<br>
@@ -160,28 +166,84 @@<br>
     }<br>
   }<br>
<br>
-  /// uninitialized_copy - Copy the range [I, E) onto the uninitialized memory<br>
-  /// starting with "Dest", constructing elements into it as needed.<br>
+  /// move - Use move-assignment to move the range [I, E) onto the<br>
+  /// objects starting with "Dest".  This is just <memory>'s<br>
+  /// std::move, but not all stdlibs actually provide that.<br>
+  template<typename It1, typename It2><br>
+  static It2 move(It1 I, It1 E, It2 Dest) {<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+    for (; I != E; ++I, ++Dest)<br>
+      *Dest = ::std::move(*I);<br>
+    return Dest;<br>
+#else<br>
+    return ::std::copy(I, E, Dest);<br>
+#endif<br>
+  }<br>
+<br>
+  /// move_backward - Use move-assignment to move the range<br>
+  /// [I, E) onto the objects ending at "Dest", moving objects<br>
+  /// in reverse order.  This is just <algorithm>'s<br>
+  /// std::move_backward, but not all stdlibs actually provide that.<br>
+  template<typename It1, typename It2><br>
+  static It2 move_backward(It1 I, It1 E, It2 Dest) {<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+    while (I != E)<br>
+      *--Dest = ::std::move(*--E);<br>
+    return Dest;<br>
+#else<br>
+    return ::std::copy_backward(I, E, Dest);<br>
+#endif<br>
+  }<br>
+<br>
+  /// uninitialized_move - Move the range [I, E) into the uninitialized<br>
+  /// memory starting with "Dest", constructing elements as needed.<br>
+  template<typename It1, typename It2><br>
+  static void uninitialized_move(It1 I, It1 E, It2 Dest) {<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+    for (; I != E; ++I, ++Dest)<br>
+      ::new ((void*) &*Dest) T(::std::move(*I));<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">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.</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">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?</blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">(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?)</blockquote>
+#else<br>
+    ::std::uninitialized_copy(I, E, Dest);<br>
+#endif<br>
+  }<br>
+<br>
+  /// uninitialized_copy - Copy the range [I, E) onto the uninitialized<br>
+  /// memory starting with "Dest", constructing elements as needed.<br>
   template<typename It1, typename It2><br>
   static void uninitialized_copy(It1 I, It1 E, It2 Dest) {<br>
     std::uninitialized_copy(I, E, Dest);<br>
   }<br>
<br>
-  /// grow - double the size of the allocated memory, guaranteeing space for at<br>
-  /// least one more element or MinSize if specified.<br>
+  /// grow - Grow the allocated memory (without initializing new<br>
+  /// elements), doubling the size of the allocated memory.<br>
+  /// Guarantees space for at least one more element, or MinSize more<br>
+  /// elements if specified.<br>
   void grow(size_t MinSize = 0);<br>
<br>
 public:<br>
   void push_back(const T &Elt) {<br>
     if (this->EndX < this->CapacityX) {<br>
     Retry:<br>
-      new (this->end()) T(Elt);<br>
+      ::new ((void*) this->end()) T(Elt);<br>
+      this->setEnd(this->end()+1);<br>
+      return;<br>
+    }<br>
+    this->grow();<br>
+    goto Retry;<br>
+  }<br>
+<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+  void push_back(T &&Elt) {<br>
+    if (this->EndX < this->CapacityX) {<br>
+    Retry:<br>
+      ::new ((void*) this->end()) T(::std::move(Elt));<br>
       this->setEnd(this->end()+1);<br>
       return;<br>
     }<br>
     this->grow();<br>
     goto Retry;<br>
   }<br>
+#endif<br>
<br>
   void pop_back() {<br>
     this->setEnd(this->end()-1);<br>
@@ -199,8 +261,8 @@<br>
     NewCapacity = MinSize;<br>
   T *NewElts = static_cast<T*>(malloc(NewCapacity*sizeof(T)));<br>
<br>
-  // Copy the elements over.<br>
-  this->uninitialized_copy(this->begin(), this->end(), NewElts);<br>
+  // Move the elements over.<br>
+  this->uninitialized_move(this->begin(), this->end(), NewElts);<br>
<br>
   // Destroy the original elements.<br>
   destroy_range(this->begin(), this->end());<br>
@@ -225,6 +287,29 @@<br>
   // No need to do a destroy loop for POD's.<br>
   static void destroy_range(T *, T *) {}<br>
<br>
+  /// move - Use move-assignment to move the range [I, E) onto the<br>
+  /// objects starting with "Dest".  For PODs, this is just memcpy.<br>
+  template<typename It1, typename It2><br>
+  static It2 move(It1 I, It1 E, It2 Dest) {<br>
+    return ::std::copy(I, E, Dest);<br></blockquote><div><br></div><div><span style="font-family:monospace">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.</span><br style="font-family:monospace"><br style="font-family:monospace"><span style="font-family:monospace">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?</span><br style="font-family:monospace"><br style="font-family:monospace"><span style="font-family:monospace">(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?)</span><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
+  }<br>
+<br>
+  /// move_backward - Use move-assignment to move the range<br>
+  /// [I, E) onto the objects ending at "Dest", moving objects<br>
+  /// in reverse order.<br>
+  template<typename It1, typename It2><br>
+  static It2 move_backward(It1 I, It1 E, It2 Dest) {<br>
+    return ::std::copy_backward(I, E, Dest);<br>
+  }<br>
+<br>
+  /// uninitialized_move - Move the range [I, E) onto the uninitialized memory<br>
+  /// starting with "Dest", constructing elements into it as needed.<br>
+  template<typename It1, typename It2><br>
+  static void uninitialized_move(It1 I, It1 E, It2 Dest) {<br>
+    // Just do a copy.<br>
+    uninitialized_copy(I, E, Dest);<br>
+  }<br>
+<br>
   /// uninitialized_copy - Copy the range [I, E) onto the uninitialized memory<br>
   /// starting with "Dest", constructing elements into it as needed.<br>
   template<typename It1, typename It2><br>
@@ -330,7 +415,11 @@<br>
   }<br>
<br>
   T pop_back_val() {<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+    T Result = ::std::move(this->back());<br>
+#else<br>
     T Result = this->back();<br>
+#endif<br>
     this->pop_back();<br>
     return Result;<br>
   }<br>
@@ -392,6 +481,36 @@<br>
     return(N);<br>
   }<br>
<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+  iterator insert(iterator I, T &&Elt) {<br>
+    if (I == this->end()) {  // Important special case for empty vector.<br>
+      this->push_back(::std::move(Elt));<br>
+      return this->end()-1;<br>
+    }<br>
+<br>
+    if (this->EndX < this->CapacityX) {<br>
+    Retry:<br>
+      ::new ((void*) this->end()) T(::std::move(this->back()));<br>
+      this->setEnd(this->end()+1);<br>
+      // Push everything else over.<br>
+      this->move_backward(I, this->end()-1, this->end());<br>
+<br>
+      // If we just moved the element we're inserting, be sure to update<br>
+      // the reference.<br>
+      T *EltPtr = &Elt;<br>
+      if (I <= EltPtr && EltPtr < this->EndX)<br>
+        ++EltPtr;<br>
+<br>
+      *I = ::std::move(*EltPtr);<br>
+      return I;<br>
+    }<br>
+    size_t EltNo = I-this->begin();<br>
+    this->grow();<br>
+    I = this->begin()+EltNo;<br>
+    goto Retry;<br>
+  }<br>
+#endif<br>
+<br>
   iterator insert(iterator I, const T &Elt) {<br>
     if (I == this->end()) {  // Important special case for empty vector.<br>
       this->push_back(Elt);<br>
@@ -400,10 +519,10 @@<br>
<br>
     if (this->EndX < this->CapacityX) {<br>
     Retry:<br>
-      new (this->end()) T(this->back());<br>
+      ::new ((void*) this->end()) T(this->back());<br>
       this->setEnd(this->end()+1);<br>
       // Push everything else over.<br>
-      std::copy_backward(I, this->end()-1, this->end());<br>
+      this->move_backward(I, this->end()-1, this->end());<br>
<br>
       // If we just moved the element we're inserting, be sure to update<br>
       // the reference.<br>
@@ -444,7 +563,7 @@<br>
       append(this->end()-NumToInsert, this->end());<br>
<br>
       // Copy the existing elements that get replaced.<br>
-      std::copy_backward(I, OldEnd-NumToInsert, OldEnd);<br>
+      this->move_backward(I, OldEnd-NumToInsert, OldEnd);<br>
<br>
       std::fill_n(I, NumToInsert, Elt);<br>
       return I;<br>
@@ -493,7 +612,7 @@<br>
       append(this->end()-NumToInsert, this->end());<br>
<br>
       // Copy the existing elements that get replaced.<br>
-      std::copy_backward(I, OldEnd-NumToInsert, OldEnd);<br>
+      this->move_backward(I, OldEnd-NumToInsert, OldEnd);<br>
<br>
       std::copy(From, To, I);<br>
       return I;<br>
@@ -519,8 +638,11 @@<br>
     return I;<br>
   }<br>
<br>
-  const SmallVectorImpl<br>
-  &operator=(const SmallVectorImpl &RHS);<br>
+  SmallVectorImpl &operator=(const SmallVectorImpl &RHS);<br>
+<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+  SmallVectorImpl &operator=(SmallVectorImpl &&RHS);<br>
+#endif<br>
<br>
   bool operator==(const SmallVectorImpl &RHS) const {<br>
     if (this->size() != RHS.size()) return false;<br>
@@ -590,7 +712,7 @@<br>
 }<br>
<br>
 template <typename T><br>
-const SmallVectorImpl<T> &SmallVectorImpl<T>::<br>
+SmallVectorImpl<T> &SmallVectorImpl<T>::<br>
   operator=(const SmallVectorImpl<T> &RHS) {<br>
   // Avoid self-assignment.<br>
   if (this == &RHS) return *this;<br>
@@ -617,6 +739,7 @@<br>
<br>
   // If we have to grow to have enough elements, destroy the current elements.<br>
   // This allows us to avoid copying them during the grow.<br>
+  // FIXME: don't do this if they're efficiently moveable.<br>
   if (this->capacity() < RHSSize) {<br>
     // Destroy current elements.<br>
     this->destroy_range(this->begin(), this->end());<br>
@@ -637,6 +760,69 @@<br>
   return *this;<br>
 }<br>
<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+template <typename T><br>
+SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {<br>
+  // Avoid self-assignment.<br>
+  if (this == &RHS) return *this;<br>
+<br>
+  // If the RHS isn't small, clear this vector and then steal its buffer.<br>
+  if (!RHS.isSmall()) {<br>
+    this->destroy_range(this->begin(), this->end());<br>
+    if (!this->isSmall()) free(this->begin());<br>
+    this->BeginX = RHS.BeginX;<br>
+    this->EndX = RHS.EndX;<br>
+    this->CapacityX = RHS.CapacityX;<br>
+    RHS.resetToSmall();<br>
+    return *this;<br>
+  }<br>
+<br>
+  // If we already have sufficient space, assign the common elements, then<br>
+  // destroy any excess.<br>
+  size_t RHSSize = RHS.size();<br>
+  size_t CurSize = this->size();<br>
+  if (CurSize >= RHSSize) {<br>
+    // Assign common elements.<br>
+    iterator NewEnd = this->begin();<br>
+    if (RHSSize)<br>
+      NewEnd = this->move(RHS.begin(), RHS.end(), NewEnd);<br>
+<br>
+    // Destroy excess elements and trim the bounds.<br>
+    this->destroy_range(NewEnd, this->end());<br>
+    this->setEnd(NewEnd);<br>
+<br>
+    // Clear the RHS.<br>
+    RHS.clear();<br>
+<br>
+    return *this;<br>
+  }<br>
+<br>
+  // If we have to grow to have enough elements, destroy the current elements.<br>
+  // This allows us to avoid copying them during the grow.<br>
+  // FIXME: this may not actually make any sense if we can efficiently move<br>
+  // elements.<br>
+  if (this->capacity() < RHSSize) {<br>
+    // Destroy current elements.<br>
+    this->destroy_range(this->begin(), this->end());<br>
+    this->setEnd(this->begin());<br>
+    CurSize = 0;<br>
+    this->grow(RHSSize);<br>
+  } else if (CurSize) {<br>
+    // Otherwise, use assignment for the already-constructed elements.<br>
+    this->move(RHS.begin(), RHS.end(), this->begin());<br>
+  }<br>
+<br>
+  // Move-construct the new elements in place.<br>
+  this->uninitialized_move(RHS.begin()+CurSize, RHS.end(),<br>
+                           this->begin()+CurSize);<br>
+<br>
+  // Set end.<br>
+  this->setEnd(this->begin()+RHSSize);<br>
+<br>
+  RHS.clear();<br>
+  return *this;<br>
+}<br>
+#endif<br>
<br>
 /// SmallVector - This is a 'vector' (really, a variable-sized array), optimized<br>
 /// for the case when the array is small.  It contains some number of elements<br>
@@ -692,6 +878,18 @@<br>
     return *this;<br>
   }<br>
<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+  SmallVector(SmallVector &&RHS) : SmallVectorImpl<T>(NumTsAvailable) {<br>
+    if (!RHS.empty())<br>
+      SmallVectorImpl<T>::operator=(::std::move(RHS));<br>
+  }<br>
+<br>
+  const SmallVector &operator=(SmallVector &&RHS) {<br>
+    SmallVectorImpl<T>::operator=(::std::move(RHS));<br>
+    return *this;<br>
+  }<br>
+#endif<br>
+<br>
 };<br>
<br>
 /// Specialize SmallVector at N=0.  This specialization guarantees<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=155979&r1=155978&r2=155979&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Compiler.h?rev=155979&r1=155978&r2=155979&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Support/Compiler.h (original)<br>
+++ llvm/trunk/include/llvm/Support/Compiler.h Wed May  2 00:39:15 2012<br>
@@ -19,6 +19,25 @@<br>
 # define __has_feature(x) 0<br>
 #endif<br>
<br>
+/// LLVM_HAS_RVALUE_REFERENCES - Does the compiler provide r-value references?<br>
+/// This implies that <utility> provides the one-argument std::move;  it<br>
+/// does not imply the existence of any other C++ library features.<br>
+#if (__has_feature(cxx_rvalue_references)   \<br>
+     || defined(__GXX_EXPERIMENTAL_CXX0X__) \<br>
+     || _MSC_VER >= 1600)<br>
+#define LLVM_USE_RVALUE_REFERENCES 1<br>
+#else<br>
+#define LLVM_USE_RVALUE_REFERENCES 0<br>
+#endif<br>
+<br>
+/// llvm_move - Expands to ::std::move if the compiler supports<br>
+/// r-value references; otherwise, expands to the argument.<br>
+#if LLVM_USE_RVALUE_REFERENCES<br>
+#define llvm_move(value) (::std::move(arg))<br>
+#else<br>
+#define llvm_move(value) (value)<br>
+#endif<br>
+<br>
 /// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked<br>
 /// into a shared library, then the class should be private to the library and<br>
 /// not accessible from outside it.  Can also be used to mark variables and<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div></div>