Introduce proper constructor and operator overloads, that perform proper
transfer of memory controlled by auto_vec.
gcc/ChangeLog:
* vec.cc (struct count_cdtors): Test utility type.
(test_auto_vec_copy_move_construct): New test.
(test_auto_vec_move_assign): Likewise.
(test_auto_vec_copy_assign): Likewise.
(vec_cc_tests): Likewise.
* vec.h (vec<T, A, vl_embed>::quick_emplace): New function.
(vec<T, va_heap, vl_ptr>::quick_emplace): Likewise.
(vec<T, va_heap, vl_ptr>::safe_emplace): Likewise.
---
gcc/vec.cc | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++
gcc/vec.h | 129 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 272 insertions(+)
diff --git a/gcc/vec.cc b/gcc/vec.cc
index 8313adde2b22b..3d7fc387ba428 100644
--- a/gcc/vec.cc
+++ b/gcc/vec.cc
@@ -640,6 +640,146 @@ test_auto_delete_vec ()
ASSERT_EQ (dtor_count, 2);
}
+struct count_cdtors{
+ static int copy_cnt, move_cnt, ctor_cnt, dtor_cnt;
+ static void reset ()
+ {
+ copy_cnt = 0;
+ move_cnt = 0;
+ ctor_cnt = 0;
+ dtor_cnt = 0;
+ }
+ static void validate (int copy, int move, int ctor, int dtor)
+ {
+ ASSERT_EQ (copy_cnt, copy);
+ ASSERT_EQ (move_cnt, move);
+ ASSERT_EQ (ctor_cnt, ctor);
+ ASSERT_EQ (dtor_cnt, dtor);
+ reset ();
+ }
+ count_cdtors () { ++ctor_cnt; }
+ ~count_cdtors () { ++dtor_cnt; }
+ count_cdtors (count_cdtors &&) { ++move_cnt; }
+ count_cdtors (const count_cdtors &) { ++copy_cnt; }
+ count_cdtors &operator= (count_cdtors &&) { return ++move_cnt, *this; }
+ count_cdtors &operator= (const count_cdtors &) { return ++copy_cnt, *this; }
+};
+int count_cdtors::copy_cnt = 0,
+ count_cdtors::move_cnt = 0,
+ count_cdtors::ctor_cnt = 0,
+ count_cdtors::dtor_cnt = 0;
+
+static void
+test_auto_vec_copy_move_construct ()
+{
+ count_cdtors::reset ();
+ {
+ count_cdtors::validate (0, 0, 0, 0);
+ auto_vec<count_cdtors, 1> test (2);
+ count_cdtors::validate (0, 0, 0, 0);
+ test.quick_grow_cleared (2);
+ count_cdtors::validate (0, 0, 2, 0);
+ auto test2 = test;
+ count_cdtors::validate (2, 0, 0, 0);
+ auto test3 = std::move (test);
+ count_cdtors::validate (0, 0, 0, 0);
+ }
+ count_cdtors::validate (0, 0, 0, 4);
+ {
+ auto_vec<count_cdtors, 2> test (2);
+ count_cdtors::validate (0, 0, 0, 0);
+ test.quick_grow_cleared (2);
+ count_cdtors::validate (0, 0, 2, 0);
+ auto test2 = test;
+ count_cdtors::validate (2, 0, 0, 0);
+ auto test3 = std::move (test);
+ count_cdtors::validate (0, 2, 0, 0);
+ }
+ count_cdtors::validate (0, 0, 0, 6);
+}
+
+static void
+test_auto_vec_move_assign ()
+{
+ {
+ auto_vec<count_cdtors, 1> from (1), to (1);
+ from.quick_grow_cleared (1);
+ to.quick_grow_cleared (1);
+ count_cdtors::validate (0, 0, 2, 0);
+ to = std::move (from);
+ count_cdtors::validate (0, 1, 0, 0);
+ }
+ count_cdtors::validate (0, 0, 0, 2);
+ {
+ auto_vec<count_cdtors, 1> from (1), to (2);
+ from.quick_grow_cleared (1);
+ to.quick_grow_cleared (2);
+ count_cdtors::validate (0, 0, 3, 0);
+ to = std::move (from);
+ count_cdtors::validate (0, 1, 0, 2);
+ }
+ count_cdtors::validate (0, 0, 0, 2);
+ {
+ auto_vec<count_cdtors, 1> from (2), to (1);
+ from.quick_grow_cleared (2);
+ to.quick_grow_cleared (1);
+ count_cdtors::validate (0, 0, 3, 0);
+ to = std::move (from);
+ count_cdtors::validate (0, 0, 0, 1);
+ }
+ count_cdtors::validate (0, 0, 0, 2);
+ {
+ auto_vec<count_cdtors, 1> from (2), to (2);
+ from.quick_grow_cleared (2);
+ to.quick_grow_cleared (2);
+ count_cdtors::validate (0, 0, 4, 0);
+ to = std::move (from);
+ count_cdtors::validate (0, 0, 0, 2);
+ }
+ count_cdtors::validate (0, 0, 0, 2);
+}
+
+static void
+test_auto_vec_copy_assign ()
+{
+ {
+ auto_vec<count_cdtors, 1> from (1), to (1);
+ from.quick_grow_cleared (1);
+ to.quick_grow_cleared (1);
+ count_cdtors::validate (0, 0, 2, 0);
+ to = from;
+ count_cdtors::validate (1, 0, 0, 0);
+ }
+ count_cdtors::validate (0, 0, 0, 2);
+ {
+ auto_vec<count_cdtors, 1> from (1), to (2);
+ from.quick_grow_cleared (1);
+ to.quick_grow_cleared (2);
+ count_cdtors::validate (0, 0, 3, 0);
+ to = from;
+ count_cdtors::validate (1, 0, 0, 1);
+ }
+ count_cdtors::validate (0, 0, 0, 2);
+ {
+ auto_vec<count_cdtors, 1> from (2), to (1);
+ from.quick_grow_cleared (2);
+ to.quick_grow_cleared (1);
+ count_cdtors::validate (0, 0, 3, 0);
+ to = from;
+ count_cdtors::validate (2, 0, 0, 1);
+ }
+ count_cdtors::validate (0, 0, 0, 4);
+ {
+ auto_vec<count_cdtors, 1> from (2), to (2);
+ from.quick_grow_cleared (2);
+ to.quick_grow_cleared (2);
+ count_cdtors::validate (0, 0, 4, 0);
+ to = from;
+ count_cdtors::validate (2, 0, 0, 0);
+ }
+ count_cdtors::validate (0, 0, 0, 4);
+}
+
/* Verify accesses to vector elements are done indirectly. */
static void
@@ -828,6 +968,9 @@ vec_cc_tests ()
test_qsort ();
test_reverse ();
test_auto_delete_vec ();
+ test_auto_vec_copy_move_construct ();
+ test_auto_vec_copy_assign ();
+ test_auto_vec_move_assign ();
test_auto_alias ();
test_string_slice_initializers ();
test_string_slice_tokenize ();
diff --git a/gcc/vec.h b/gcc/vec.h
index 9d25678029832..fcab26d5c418d 100644
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -620,6 +620,8 @@ public:
void splice (const vec &);
void splice (const vec *src);
T *quick_push (const T &);
+ template<typename ...Ts>
+ T *quick_emplace (Ts &&...);
using pop_ret_type
= typename std::conditional <std::is_trivially_destructible <T>::value,
T &, void>::type;
@@ -1053,6 +1055,16 @@ vec<T, A, vl_embed>::quick_push (const T &obj)
return slot;
}
+template<typename T, typename A>
+template<typename ...Ts>
+inline T *
+vec<T, A, vl_embed>::quick_emplace (Ts &&...obj)
+{
+ gcc_checking_assert (space (1));
+ T *slot = &address ()[m_vecpfx.m_num++];
+ ::new (static_cast<void*>(slot)) T ((Ts &&)obj...);
+ return slot;
+}
/* Pop and return a reference to the last element off the end of the
vector. If T has non-trivial destructor, this method just pops
@@ -1623,7 +1635,11 @@ public:
void splice (const vec &);
void safe_splice (const vec & CXX_MEM_STAT_INFO);
T *quick_push (const T &);
+ template<typename ...Ts>
+ T *quick_emplace (Ts &&...);
T *safe_push (const T &CXX_MEM_STAT_INFO);
+ template<typename ...Ts>
+ T *safe_emplace (Ts &&...);
using pop_ret_type
= typename std::conditional <std::is_trivially_destructible <T>::value,
T &, void>::type;
@@ -1691,6 +1707,91 @@ public:
size_t off = (char *) &m_auto - (char *) this;
this->m_vec = (vec<T, va_heap, vl_embed> *) ((char *) this + off);
}
+ auto_vec (size_t s CXX_MEM_STAT_INFO, T fill): auto_vec (s)
+ {
+ for (size_t i = 0; i != s; ++i)
+ this->quick_push (fill);
+ }
+
+ auto_vec (auto_vec &&r)
+ noexcept (std::is_nothrow_move_constructible<T>::value)
+ : auto_vec ()
+ {
+ if (!r.using_auto_storage ())
+ {
+ this->m_vec = std::exchange (r.m_vec, nullptr);
+ return;
+ }
+ for (auto &v: r)
+ this->quick_emplace (std::move (v));
+ }
+
+ auto_vec &operator= (auto_vec &&r)
+ noexcept (std::is_nothrow_move_constructible<T>::value)
+ {
+ if (!r.using_auto_storage () && !this->using_auto_storage ())
+ {
+ std::swap (this->m_vec, r.m_vec);
+ r.release ();
+ }
+ else if (!r.using_auto_storage () && this->using_auto_storage ())
+ {
+ auto data = std::exchange (r.m_vec, nullptr);
+ std::exchange (this->m_vec, data)->truncate (0);
+ }
+ else if (r.using_auto_storage () && !this->using_auto_storage ())
+ {
+ /* To avoid losing temporaries, we have to fill the internal array
+ first, and only then destroy the residual elems. */
+ m_auto.embedded_init (N, 0, 1);
+ vec<T, va_heap> replacement;
+ size_t off = (char *) &m_auto - (char *) this;
+ replacement.m_vec = (vec<T, va_heap, vl_embed> *) ((char *) this + off);
+ for (auto &v: r)
+ replacement.quick_emplace (std::move (v));
+ std::exchange ((vec<T, va_heap> &)*this, replacement).release ();
+ }
+ else if (r.using_auto_storage () && this->using_auto_storage ())
+ {
+ /* The most complicated case: move-assign all the elements that are live
+ in both, rthen move-construct or destruct the rest. */
+ int a = this->length (), b = r.length ();
+ int i;
+ for (i = 0; i < a && i < b; ++i)
+ (*this)[i] = std::move (r[i]);
+ for (; i < b; ++i)
+ this->quick_emplace (std::move (r[i]));
+ this->truncate (b);
+ }
+ else gcc_unreachable ();
+ return *this;
+ }
+
+ auto_vec (const auto_vec &r): auto_vec (r.length ())
+ {
+ for (auto &v: r)
+ this->quick_push (v);
+ }
+
+ auto_vec &operator= (const auto_vec &r)
+ {
+ if (r.length () <= this->allocated ())
+ {
+ int a = this->length (), b = r.length ();
+ int i;
+ for (i = 0; i < a && i < b; ++i)
+ (*this)[i] = r[i];
+ for (; i < b; ++i)
+ this->quick_emplace (r[i]);
+ this->truncate (b);
+ }
+ else
+ {
+ auto_vec fresh = r;
+ *this = std::move (fresh);
+ }
+ return *this;
+ }
~auto_vec ()
{
@@ -2082,6 +2183,19 @@ vec<T, va_heap, vl_ptr>::quick_push (const T &obj)
}
+/* Construct a new element from constructor ARGS at the end of the vector.
+ There must be sufficient space in the vector. Return a pointer to the slot
+ where the element was inserted. */
+
+template<typename T>
+template<typename ...Ts>
+inline T *
+vec<T, va_heap, vl_ptr>::quick_emplace (Ts &&...args)
+{
+ return m_vec->quick_emplace ((Ts &&)args...);
+}
+
+
/* Push a new element OBJ onto the end of this vector. Reallocates
the embedded vector, if needed. Return a pointer to the slot where
OBJ was inserted. */
@@ -2095,6 +2209,21 @@ vec<T, va_heap, vl_ptr>::safe_push (const T &obj
MEM_STAT_DECL)
}
+/* Construct a new element from constructor ARGS at the end of the vector.
+ Reallocates the embedded vector, if needed. Return a pointer to the slot
+ where the element was inserted. */
+
+template<typename T>
+template<typename ...Ts>
+inline T *
+vec<T, va_heap, vl_ptr>::safe_emplace (Ts &&...args)
+{
+ reserve (1, false PASS_MEM_STAT);
+ return quick_emplace ((Ts &&)args...);
+}
+
+
+
/* Pop and return a reference to the last element off the end of the
vector. If T has non-trivial destructor, this method just pops
last element and returns void. */
--
2.54.0