Index: llvm/include/llvm/ADT/SmallVector.h =================================================================== --- llvm/include/llvm/ADT/SmallVector.h +++ llvm/include/llvm/ADT/SmallVector.h @@ -136,11 +136,20 @@ this->Size = this->Capacity = 0; // FIXME: Setting Capacity to 0 is suspect. } - void assertSafeToPush(const void *Elt) { - assert( - (Elt < begin() || Elt >= end() || this->size() < this->capacity()) && - "Attempting to push_back to the vector an element of the vector without" - " enough space reserved"); + /// Check whether Elt will be invalidated by resizing the vector to NewSize. + void assertSafeToReferenceAfterResize(const void *Elt, size_t NewSize) { + assert((Elt >= this->end() || + (NewSize <= this->size() + ? Elt < this->begin() + NewSize + : (Elt < this->begin() || NewSize <= this->capacity()))) && + "Attempting to reference an element of the vector in an operation " + "that invalidates it"); + } + + /// Check whether Elt will be invalidated by increasing the size of the + /// vector by N. + void assertSafeToAdd(const void *Elt, size_t N = 1) { + return this->assertSafeToReferenceAfterResize(Elt, this->size() + N); } public: @@ -226,7 +235,14 @@ (is_trivially_move_constructible::value) && std::is_trivially_destructible::value> class SmallVectorTemplateBase : public SmallVectorTemplateCommon { +public: + /// Take parameters by reference (not by value) since T is not trivially + /// copyable. + static constexpr bool TakesParamByValue = false; + protected: + using ValueParamT = const T &; + SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon(Size) {} static void destroy_range(T *S, T *E) { @@ -257,8 +273,9 @@ void grow(size_t MinSize = 0); public: - void push_back(const T &Elt) { - this->assertSafeToPush(&Elt); + void push_back(ValueParamT Elt) { + if (!TakesParamByValue) + this->assertSafeToAdd(&Elt); if (LLVM_UNLIKELY(this->size() >= this->capacity())) this->grow(); ::new ((void*) this->end()) T(Elt); @@ -266,7 +283,7 @@ } void push_back(T &&Elt) { - this->assertSafeToPush(&Elt); + this->assertSafeToAdd(&Elt); if (LLVM_UNLIKELY(this->size() >= this->capacity())) this->grow(); ::new ((void*) this->end()) T(::std::move(Elt)); @@ -319,7 +336,16 @@ /// skipping destruction. template class SmallVectorTemplateBase : public SmallVectorTemplateCommon { +public: + /// Take parameters by value when T is small since it's trivially copyable. + static constexpr bool TakesParamByValue = + std::conditional::type::value; + protected: + using ValueParamT = + typename std::conditional::type; + SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon(Size) {} // No need to do a destroy loop for POD's. @@ -361,8 +387,9 @@ void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); } public: - void push_back(const T &Elt) { - this->assertSafeToPush(&Elt); + void push_back(ValueParamT Elt) { + if (!TakesParamByValue) + this->assertSafeToAdd(&Elt); if (LLVM_UNLIKELY(this->size() >= this->capacity())) this->grow(); memcpy(reinterpret_cast(this->end()), &Elt, sizeof(T)); @@ -384,7 +411,11 @@ using reference = typename SuperClass::reference; using size_type = typename SuperClass::size_type; + using SmallVectorTemplateBase::TakesParamByValue; + protected: + using ValueParamT = typename SmallVectorTemplateBase::ValueParamT; + // Default ctor - Initialize to empty. explicit SmallVectorImpl(unsigned N) : SmallVectorTemplateBase(N) {} @@ -417,7 +448,9 @@ } } - void resize(size_type N, const T &NV) { + void resize(size_type N, ValueParamT NV) { + if (!TakesParamByValue) + this->assertSafeToReferenceAfterResize(&NV, N); if (N < this->size()) { this->destroy_range(this->begin()+N, this->end()); this->set_size(N); @@ -463,7 +496,9 @@ } /// Append \p NumInputs copies of \p Elt to the end. - void append(size_type NumInputs, const T &Elt) { + void append(size_type NumInputs, ValueParamT Elt) { + if (!TakesParamByValue) + this->assertSafeToAdd(&Elt, NumInputs); if (NumInputs > this->capacity() - this->size()) this->grow(this->size()+NumInputs); @@ -478,7 +513,9 @@ // FIXME: Consider assigning over existing elements, rather than clearing & // re-initializing them - for all assign(...) variants. - void assign(size_type NumElts, const T &Elt) { + void assign(size_type NumElts, ValueParamT Elt) { + if (!TakesParamByValue) + this->assertSafeToReferenceAfterResize(&Elt, 0); clear(); if (this->capacity() < NumElts) this->grow(NumElts); @@ -533,7 +570,14 @@ return(N); } - iterator insert(iterator I, T &&Elt) { + // Use a mock template argument to allow use of enable_if for disabling this + // version of insert when ValueParamT is the same as T. + template + iterator insert(IteratorT I1, T &&Elt, + typename std::enable_if_t< + std::is_convertible::value && + !TakesParamByValue> * = 0) { + iterator I = I1; if (I == this->end()) { // Important special case for empty vector. this->push_back(::std::move(Elt)); return this->end()-1; @@ -542,6 +586,10 @@ assert(I >= this->begin() && "Insertion iterator is out of bounds."); assert(I <= this->end() && "Inserting past the end of the vector."); + // Check that adding an element won't invalidate Elt. + if (!TakesParamByValue) + this->assertSafeToAdd(&Elt); + if (this->size() >= this->capacity()) { size_t EltNo = I-this->begin(); this->grow(); @@ -563,7 +611,7 @@ return I; } - iterator insert(iterator I, const T &Elt) { + iterator insert(iterator I, ValueParamT Elt) { if (I == this->end()) { // Important special case for empty vector. this->push_back(Elt); return this->end()-1; @@ -572,6 +620,10 @@ assert(I >= this->begin() && "Insertion iterator is out of bounds."); assert(I <= this->end() && "Inserting past the end of the vector."); + // Check that adding an element won't invalidate Elt. + if (!TakesParamByValue) + this->assertSafeToAdd(&Elt); + if (this->size() >= this->capacity()) { size_t EltNo = I-this->begin(); this->grow(); @@ -583,16 +635,17 @@ this->set_size(this->size() + 1); // If we just moved the element we're inserting, be sure to update - // the reference. + // the reference. The check for TakesParamByValue is redundant but can + // be resolved at compile-time. const T *EltPtr = &Elt; - if (I <= EltPtr && EltPtr < this->end()) + if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end()) ++EltPtr; *I = *EltPtr; return I; } - iterator insert(iterator I, size_type NumToInsert, const T &Elt) { + iterator insert(iterator I, size_type NumToInsert, ValueParamT Elt) { // Convert iterator to elt# to avoid invalidating iterator when we reserve() size_t InsertElt = I - this->begin(); @@ -604,6 +657,10 @@ assert(I >= this->begin() && "Insertion iterator is out of bounds."); assert(I <= this->end() && "Inserting past the end of the vector."); + // Check that adding NumToInsert elements won't invalidate Elt. + if (!TakesParamByValue) + this->assertSafeToAdd(&Elt, NumToInsert); + // Ensure there is enough space. reserve(this->size() + NumToInsert); @@ -905,6 +962,8 @@ template class LLVM_GSL_OWNER SmallVector : public SmallVectorImpl, SmallVectorStorage { + using ValueParamT = typename SmallVectorImpl::ValueParamT; + public: SmallVector() : SmallVectorImpl(N) {} @@ -913,8 +972,8 @@ this->destroy_range(this->begin(), this->end()); } - explicit SmallVector(size_t Size, const T &Value = T()) - : SmallVectorImpl(N) { + explicit SmallVector(size_t Size, ValueParamT Value = T()) + : SmallVectorImpl(N) { this->assign(Size, Value); } Index: llvm/unittests/ADT/SmallVectorTest.cpp =================================================================== --- llvm/unittests/ADT/SmallVectorTest.cpp +++ llvm/unittests/ADT/SmallVectorTest.cpp @@ -252,8 +252,13 @@ this->theVector.push_back(Constructable(2)); this->assertValuesInOrder(this->theVector, 2u, 1, 2); - // Insert at beginning - this->theVector.insert(this->theVector.begin(), this->theVector[1]); + // Insert at beginning. Take a copy when necessary to avoid having the + // element invalidated by growth. + if (this->theVector.capacity() < 3) + this->theVector.insert(this->theVector.begin(), + Constructable(this->theVector[1])); + else + this->theVector.insert(this->theVector.begin(), this->theVector[1]); this->assertValuesInOrder(this->theVector, 3u, 2, 1, 2); // Pop one element @@ -999,4 +1004,131 @@ EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2})); } +template +class SmallVectorTakesParameterByValueTest : public SmallVectorTestBase { +protected: + bool TakesParamByValue = VectorT::TakesParamByValue; + const char *AssertionMessage = + "Attempting to reference an element of the vector in an operation \" " + "\"that invalidates it"; + + VectorT V; + + template + static unsigned NumBuiltinElts(const SmallVector &) { + return N; + } + + void SetUp() override { + SmallVectorTestBase::SetUp(); + + // Fill up the small size so that insertions move the elements. + V.push_back(0); + } +}; + +static_assert(SmallVectorImpl::TakesParamByValue, + "int should be trivially copyable and small enough"); +static_assert(!SmallVectorImpl::TakesParamByValue, + "Constructable should not be trivially copyable"); +using SmallVectorTakesParameterByValueTestTypes = + ::testing::Types, SmallVector>; + +TYPED_TEST_CASE(SmallVectorTakesParameterByValueTest, + SmallVectorTakesParameterByValueTestTypes); + +TYPED_TEST(SmallVectorTakesParameterByValueTest, PushBack) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.push_back(V.back()); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.push_back(V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, PushBackMoved) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.push_back(std::move(V.back())); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.push_back(std::move(V.back())), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, Resize) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.resize(2, V.back()); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.resize(2, V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, Append) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.append(1, V.back()); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.append(1, V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, Assign) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.assign(1, V.back()); + V.assign(2, V.back()); + V.assign(3, V.back()); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + // Regardless of capacity, assign should never reference an internal element. + EXPECT_DEATH(V.assign(1, V.back()), this->AssertionMessage); + EXPECT_DEATH(V.assign(2, V.back()), this->AssertionMessage); + EXPECT_DEATH(V.assign(3, V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, Insert) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.insert(V.begin(), V.back()); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, InsertMoved) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.insert(V.begin(), std::move(V.back())); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), std::move(V.back())), + this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorTakesParameterByValueTest, InsertN) { + auto &V = this->V; + if (this->TakesParamByValue) { + V.insert(V.begin(), 2, V.back()); + return; + } +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), 2, V.back()), this->AssertionMessage); +#endif +} + } // end namespace Index: llvm/utils/TableGen/CodeGenSchedule.cpp =================================================================== --- llvm/utils/TableGen/CodeGenSchedule.cpp +++ llvm/utils/TableGen/CodeGenSchedule.cpp @@ -1540,6 +1540,7 @@ if (SchedRW.IsVariadic) { unsigned OperIdx = RWSequences.size()-1; // Make N-1 copies of this transition's last sequence. + RWSequences.reserve(RWSequences.size() + SelectedRWs.size() - 1); RWSequences.insert(RWSequences.end(), SelectedRWs.size() - 1, RWSequences[OperIdx]); // Push each of the N elements of the SelectedRWs onto a copy of the last @@ -1625,6 +1626,7 @@ // any transition with compatible CPU ID. // In such case we create new empty transition with zero (AnyCPU) // index. + TransVec.reserve(TransVec.size() + 1); TransVec.emplace_back(TransVec[StartIdx].PredTerm); TransVec.back().ReadSequences.emplace_back(); CollectAndAddVariants(TransVec.size() - 1, SchedRW);