[llvm] r178319 - Revert r178166. According to Howard, this code is actually ok.
Howard Hinnant
hhinnant at apple.com
Wed Apr 17 07:25:15 PDT 2013
On Apr 17, 2013, at 8:33 AM, David Blaikie <dblaikie at gmail.com> wrote:
>
> On Mar 29, 2013 11:16 AM, "Dan Gohman" <dan433584 at gmail.com> wrote:
> >
> > Author: djg
> > Date: Thu Mar 28 19:13:08 2013
> > New Revision: 178319
> >
> > URL: http://llvm.org/viewvc/llvm-project?rev=178319&view=rev
> > Log:
> > Revert r178166. According to Howard, this code is actually ok.
>
> Well. It's technically correct according to the standard, but do we know how well the standard is implemented in the implementations wee care about? I believe this was found as a bug when using at least one version of libstdc++...
I started investigating this. This file came into existence in 2012. I'm quite sure that gcc-4.0 (2006) nor gcc-4.2 (2007) had this bug. It could have been introduced after one of those two versions, I'm not sure.
In order for a vector implementation to exhibit such a bug it would have to:
1. Allocate a new buffer.
2. Copy/move everything from the old buffer to the new buffer.
3. Destruct/deallocate the old buffer.
4. Construct the new element in the new buffer.
This implementation would not only cause the self-referencing bug, it would also not meet the exception safety guarantee specified by the standard: If an exception is thrown by push_back, there shall be no effects. This exception safety specification dates back to C++98.
One possibility is that the bug was introduced with the introduction of move semantics, and I note that PredTransition has an implicitly generated move constructor (if compiled with a 2011 or later compiler in -std=c++11):
1. Allocate a new buffer.
2. Move everything from the old buffer to the new buffer.
3. Construct the new element in the new buffer.
4. Destruct/deallocate the old buffer.
This is quite possible as this would have been the natural implementation of push_back (the realloc branch) for C++98/03. In C++11 the above algorithm also fails to meet the C++98 exception safety guarantee because if 3) throws, the move in 2) has altered the old buffer. Though I guess you could catch the exception and move everything back if you have a noexcept move assignment operator.
If one wants to protect against such a std::vector bug, I recommend:
TransVec.push_back(PredTransition(TransVec[TransIdx]));
This is better than the original workaround of:
PredTransition Trans = TransVec[TransIdx];
TransVec.push_back(Trans);
Because the latter costs 2 copy constructions whereas the former costs 1 copy construction and 1 move construction.
If you don't need to work around such a std::vector bug:
TransVec.push_back(TransVec[TransIdx]);
the cost is 1 copy construction.
While investigating this I took a look at PredTransition:
struct PredCheck {
bool IsRead;
unsigned RWIdx;
Record *Predicate;
PredCheck(bool r, unsigned w, Record *p): IsRead(r), RWIdx(w), Predicate(p) {}
};
// A Predicate transition is a list of RW sequences guarded by a PredTerm.
struct PredTransition {
// A predicate term is a conjunction of PredChecks.
SmallVector<PredCheck, 4> PredTerm;
SmallVector<SmallVector<unsigned,4>, 16> WriteSequences;
SmallVector<SmallVector<unsigned,4>, 16> ReadSequences;
SmallVector<unsigned, 4> ProcIndices;
};
On a 64 bit platform, sizeof(PredTransition) is 1.4Kb. The reason that number is important is because the cost of a move construction or move assignment is in general directly proportional to sizeof. And when you do vector<PredTransition>, you are inviting a great many move constructions and move assignments of PredTransition.
If one were to substitute std::vector for SmallVector in PredTransition, then sizeof(PredTransition) drops from 1.4Kb to 0.09Kb, or about 15X smaller, and consequently, move operations that are about 15X faster.
Disclaimer: I have not done any timings here. This is all back-of-the-envelope estimations. However in this particular use case, I suspect that SmallVector may be a significant pessimization, instead of an optimization. Only by measuring would one know for sure.
Howard
> >
> > Modified:
> > llvm/trunk/utils/TableGen/CodeGenSchedule.cpp
> >
> > Modified: llvm/trunk/utils/TableGen/CodeGenSchedule.cpp
> > URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/TableGen/CodeGenSchedule.cpp?rev=178319&r1=178318&r2=178319&view=diff
> > ==============================================================================
> > --- llvm/trunk/utils/TableGen/CodeGenSchedule.cpp (original)
> > +++ llvm/trunk/utils/TableGen/CodeGenSchedule.cpp Thu Mar 28 19:13:08 2013
> > @@ -1105,9 +1105,7 @@ void PredTransitions::getIntersectingVar
> > // Push another copy of the current transition for more variants.
> > Variant.TransVecIdx = TransVec.size();
> > IntersectingVariants.push_back(Variant);
> > -
> > - PredTransition Trans = TransVec[TransIdx];
> > - TransVec.push_back(Trans);
> > + TransVec.push_back(TransVec[TransIdx]);
> > }
> > }
> > }
> >
> >
> > _______________________________________________
> > llvm-commits mailing list
> > llvm-commits at cs.uiuc.edu
> > http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list