[cfe-commits] [PATCH] Smarter implicit copy-construction/copy-assignment.
John McCall
rjmccall at apple.com
Thu Jan 31 19:55:34 PST 2013
On Jan 11, 2013, at 7:05 PM, Lang Hames <lhames at gmail.com> wrote:
> At present, if a class contains any Non-POD members we perform element-wise copies for each field when generating the implicit copy-constructor and implicit copy-assignment operator (with an optimization for array members).
>
> This patch changes this behavior - adjacent POD members will be memcpy'd, with Non-POD members output as usual.
>
> This is an initial implementation that I'd like to get in tree. Obvious deficiencies are:
>
> It punts entirely when LangOpts.getGC != None.
> It doesn't handle inner classes/unions.
> It doesn't attach any TBAA info, even when all members are the same type.
> It isn't particularly smart about when it falls back on element-wise copies (at the moment it emits a memcpy any time it finds more than one POD field).
>
> These should all be easy to test and remedy once the code's in tree though.
>
> Does anybody see any problems with this?
> Style comments?
>
> Feedback much appreciated.
+void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr,
+ llvm::Value *SrcPtr,
+ CharUnits Size, CharUnits Alignment){
Spacing on ){. Also, you might as well call this emitMemcpy, and I'm not sure
why you made it a method on CodeGenFunction, because I actually don't want
people accidentally using it instead of the proper one just because it's more
convenient.
As Richard mentioned, please prefer lowercase function names in new code.
+ QualType FieldType = F->getType();
+ if (FieldType.isVolatileQualified() || FieldType.isObjCGCWeak() ||
+ FieldType.isObjCGCStrong() ||
+ FieldType.hasStrongOrWeakObjCLifetime())
+ return false;
Please grab the Qualifiers once and then query it.
Also, GC qualification is unfortunately more complicated than just
querying the type. It is not worth trying to get this right; just abandon this
optimization in either of the GC-enabled modes.
+ void AddMemcpyableField(FieldDecl *F) {
+ if (FirstField == 0) {
+ FirstField = F;
+ LastField = F;
+ FirstFieldOffset = RecLayout.getFieldOffset(F->getFieldIndex());
+ LastFieldOffset = FirstFieldOffset;
+ LastAddedFieldIndex = F->getFieldIndex();
+ return;
+ }
+
+ assert(F->getFieldIndex() == LastAddedFieldIndex + 1 &&
+ "Cannot aggregate non-contiguous fields.");
+ LastAddedFieldIndex = F->getFieldIndex();
+
+ // The 'first' and 'last' fields are chosen by offset, rather than field
+ // index. This allows the code to support bitfields, as well as regular
+ // fields.
+ uint64_t FOffset = RecLayout.getFieldOffset(F->getFieldIndex());
+ if (FOffset < FirstFieldOffset) {
+ FirstField = F;
+ FirstFieldOffset = FOffset;
+ }
+ if (FOffset > LastFieldOffset) {
+ LastField = F;
+ LastFieldOffset = FOffset;
+ }
+ }
I feel confident that you can organize this so that it is not quite so
much of a giant blob of code.
CharUnits GetMemcpySize() const {
+ unsigned LastFieldSize =
+ CGF.getContext().getTypeInfo(LastField->getType()).first;
+ uint64_t MemcpySizeBits =
+ LastFieldOffset + LastFieldSize - FirstFieldOffset;
+ CharUnits MemcpySize =
+ CGF.getContext().toCharUnitsFromBits(MemcpySizeBits);
+ return MemcpySize;
Do you need to round up here?
+ CodeGenFunction& GetCGF() { return CGF; }
+
+ const CodeGenFunction& GetCGF() const { return CGF; }
Just make CGF a protected member.
+ bool MemberInitIsMemcpyable(CXXCtorInitializer *MemberInit) const {
isMemberInitMemcpyable
+ virtual bool BailOut() = 0;
+
An excellent place to document the meaning of the return type.
+ // Bail out on non-POD, not-trivially-constructable members.
+ if (!FieldType.isPODType(GetCGF().getContext()) &&
+ (!CE || !CE->getConstructor()->isTrivial()))
+ return false;
If you're calling a trivial copy/move constructor, it does not matter
whether the type is POD. Test case: give a field a type with a trivial
copy constructor and non-trivial destructor, or more realistically a
non-trivial default constructor.
+ void EmitConstructorCopy(CXXCtorInitializer *MemberInit) {
addMemberInitializer
+ // If MemberInit can't be rolled into the memcpy, emit a memcpy for the
+ // currently aggregated fields, then emit an initializer for MemberInit.
+ EmitMemcpy();
+ AggregatedInits.clear();
Shouldn't EmitMemcpy() have an invariant that AggregatedInits
is empty afterwards?
+ virtual bool BailOut() {
+ if (AggregatedStmts.size() == 1) {
+ for (unsigned i = 0; i < AggregatedStmts.size(); ++i)
+ GetCGF().EmitStmt(AggregatedStmts[i]);
+ return true;
+ }
+ return false;
+ }
Restructure to reduce nesting.
+ assert(RootS->getStmtClass() == Stmt::CompoundStmtClass &&
+ "Body of an implicit assignment operator should be compound stmt.");
+ const CompoundStmt &RootCS = cast<CompoundStmt>(*RootS);
The body of this assert is just isa<CompoundStmt>(RootS).
Also, please leave it as a pointer instead of making it a reference.
+// EmitFunctionBody(Args);
Commented-out code.
+ } else if (FD->isImplicit() && isa<CXXMethodDecl>(FD) &&
+ cast<CXXMethodDecl>(FD)->isCopyAssignmentOperator()) {
You should be checking isImplicitlyDefined(), not isImplicit(). C++11
test case: A &operator=(const A&) = default;
Either a copy or a move assignment would be fine. Also, knowing
that it's an implicit assignment operator is sufficient; you can just
test (FD->getOverloadedOperator() == OO_Equal).
John.
More information about the cfe-commits
mailing list