[cfe-commits] [PATCH] Smarter implicit copy-construction/copy-assignment.
Lang Hames
lhames at gmail.com
Fri Feb 1 12:18:46 PST 2013
Hi Richard, John,
Thanks very much for the feedback - an updated patch is in the works.
Cheers,
Lang.
On Thu, Jan 31, 2013 at 7:55 PM, John McCall <rjmccall at apple.com> wrote:
> 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.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130201/ba5a783a/attachment.html>
More information about the cfe-commits
mailing list