diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2015-04-29 19:26:57 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2015-04-29 19:26:57 +0000 |
commit | 419bd0941514bad6fa9d66e6f76360791389e7e0 (patch) | |
tree | eac43af91c8b6458ab4dc6478c239e12534894fd | |
parent | ae7e4995ca2c506200f7673d9161df2d851582fb (diff) | |
download | bcm5719-llvm-419bd0941514bad6fa9d66e6f76360791389e7e0.tar.gz bcm5719-llvm-419bd0941514bad6fa9d66e6f76360791389e7e0.zip |
PR23373: A defaulted union copy constructor that is not trivial must still be
emitted as a memcpy.
llvm-svn: 236142
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 11 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 62 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprAgg.cpp | 3 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprCXX.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 8 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/copy-constructor-synthesis-2.cpp | 22 |
6 files changed, 72 insertions, 36 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 280ba57e327..d1ec7aea1d2 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3727,8 +3727,9 @@ static bool HandleFunctionCall(SourceLocation CallLoc, // Skip this for non-union classes with no fields; in that case, the defaulted // copy/move does not actually read the object. const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee); - if (MD && MD->isDefaulted() && MD->isTrivial() && - (MD->getParent()->isUnion() || hasFields(MD->getParent()))) { + if (MD && MD->isDefaulted() && + (MD->getParent()->isUnion() || + (MD->isTrivial() && hasFields(MD->getParent())))) { assert(This && (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())); LValue RHS; @@ -3792,11 +3793,9 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, // Skip this for empty non-union classes; we should not perform an // lvalue-to-rvalue conversion on them because their copy constructor does not // actually read them. - if (Definition->isDefaulted() && - ((Definition->isCopyConstructor() && Definition->isTrivial()) || - (Definition->isMoveConstructor() && Definition->isTrivial())) && + if (Definition->isDefaulted() && Definition->isCopyOrMoveConstructor() && (Definition->getParent()->isUnion() || - hasFields(Definition->getParent()))) { + (Definition->isTrivial() && hasFields(Definition->getParent())))) { LValue RHS; RHS.setFrom(Info.Ctx, ArgValues[0]); return handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index e3d4601aaff..1d63512d39b 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -540,6 +540,23 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF, CGF.EmitBlock(AfterFor, true); } +static bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) { + auto *CD = dyn_cast<CXXConstructorDecl>(D); + if (!(CD && CD->isCopyOrMoveConstructor()) && + !D->isCopyAssignmentOperator() && !D->isMoveAssignmentOperator()) + return false; + + // We can emit a memcpy for a trivial copy or move constructor/assignment. + if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding()) + return true; + + // We *must* emit a memcpy for a defaulted union copy or move op. + if (D->getParent()->isUnion() && D->isDefaulted()) + return true; + + return false; +} + static void EmitMemberInitializer(CodeGenFunction &CGF, const CXXRecordDecl *ClassDecl, CXXCtorInitializer *MemberInit, @@ -581,7 +598,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, QualType BaseElementTy = CGF.getContext().getBaseElementType(Array); CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit()); if (BaseElementTy.isPODType(CGF.getContext()) || - (CE && CE->getConstructor()->isTrivial())) { + (CE && isMemcpyEquivalentSpecialMember(CE->getConstructor()))) { unsigned SrcArgIndex = CGF.CGM.getCXXABI().getSrcArgforCopyCtor(Constructor, Args); llvm::Value *SrcPtr @@ -1021,8 +1038,8 @@ namespace { QualType FieldType = Field->getType(); CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit()); - // Bail out on non-POD, not-trivially-constructable members. - if (!(CE && CE->getConstructor()->isTrivial()) && + // Bail out on non-memcpyable, not-trivially-copyable members. + if (!(CE && isMemcpyEquivalentSpecialMember(CE->getConstructor())) && !(FieldType.isTriviallyCopyableType(CGF.getContext()) || FieldType->isReferenceType())) return false; @@ -1127,9 +1144,7 @@ namespace { return Field; } else if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(S)) { CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MCE->getCalleeDecl()); - if (!(MD && (MD->isCopyAssignmentOperator() || - MD->isMoveAssignmentOperator()) && - MD->isTrivial())) + if (!(MD && isMemcpyEquivalentSpecialMember(MD))) return nullptr; MemberExpr *IOA = dyn_cast<MemberExpr>(MCE->getImplicitObjectArgument()); if (!IOA) @@ -1732,18 +1747,23 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, bool ForVirtualBase, bool Delegating, llvm::Value *This, const CXXConstructExpr *E) { - // If this is a trivial constructor, just emit what's needed. - if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding()) { - if (E->getNumArgs() == 0) { - // Trivial default constructor, no codegen required. - assert(D->isDefaultConstructor() && - "trivial 0-arg ctor not a default ctor"); - return; - } + // C++11 [class.mfct.non-static]p2: + // If a non-static member function of a class X is called for an object that + // is not of type X, or of a type derived from X, the behavior is undefined. + // FIXME: Provide a source location here. + EmitTypeCheck(CodeGenFunction::TCK_ConstructorCall, SourceLocation(), This, + getContext().getRecordType(D->getParent())); + if (D->isTrivial() && D->isDefaultConstructor()) { + assert(E->getNumArgs() == 0 && "trivial default ctor with args"); + return; + } + + // If this is a trivial constructor, just emit what's needed. If this is a + // union copy constructor, we must emit a memcpy, because the AST does not + // model that copy. + if (isMemcpyEquivalentSpecialMember(D)) { assert(E->getNumArgs() == 1 && "unexpected argcount for trivial ctor"); - assert(D->isCopyOrMoveConstructor() && - "trivial 1-arg ctor not a copy/move ctor"); const Expr *Arg = E->getArg(0); QualType SrcTy = Arg->getType(); @@ -1753,13 +1773,6 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, return; } - // C++11 [class.mfct.non-static]p2: - // If a non-static member function of a class X is called for an object that - // is not of type X, or of a type derived from X, the behavior is undefined. - // FIXME: Provide a source location here. - EmitTypeCheck(CodeGenFunction::TCK_ConstructorCall, SourceLocation(), This, - getContext().getRecordType(D->getParent())); - CallArgList Args; // Push the this ptr. @@ -1784,8 +1797,7 @@ void CodeGenFunction::EmitSynthesizedCXXCopyCtorCall(const CXXConstructorDecl *D, llvm::Value *This, llvm::Value *Src, const CXXConstructExpr *E) { - if (D->isTrivial() && - !D->getParent()->mayInsertExtraPadding()) { + if (isMemcpyEquivalentSpecialMember(D)) { assert(E->getNumArgs() == 1 && "unexpected argcount for trivial ctor"); assert(D->isCopyOrMoveConstructor() && "trivial 1-arg ctor not a copy/move ctor"); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index b139ef299ff..2ad5ec3994f 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1415,7 +1415,8 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, assert((Record->hasTrivialCopyConstructor() || Record->hasTrivialCopyAssignment() || Record->hasTrivialMoveConstructor() || - Record->hasTrivialMoveAssignment()) && + Record->hasTrivialMoveAssignment() || + Record->isUnion()) && "Trying to aggregate-copy a type without a trivial copy/move " "constructor or assignment operator"); // Ignore empty classes in C++. diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 4bffad35797..c4f509a8e0a 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -173,7 +173,7 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( This = EmitLValue(Base).getAddress(); - if (MD->isTrivial()) { + if (MD->isTrivial() || (MD->isDefaulted() && MD->getParent()->isUnion())) { if (isa<CXXDestructorDecl>(MD)) return RValue::get(nullptr); if (isa<CXXConstructorDecl>(MD) && cast<CXXConstructorDecl>(MD)->isDefaultConstructor()) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index b06b509de1e..8599a0213d2 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -10214,7 +10214,9 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, // Assign non-static members. for (auto *Field : ClassDecl->fields()) { - if (Field->isUnnamedBitfield()) + // FIXME: We should form some kind of AST representation for the implied + // memcpy in a union copy operation. + if (Field->isUnnamedBitfield() || Field->getParent()->isUnion()) continue; if (Field->isInvalidDecl()) { @@ -10644,7 +10646,9 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, // Assign non-static members. for (auto *Field : ClassDecl->fields()) { - if (Field->isUnnamedBitfield()) + // FIXME: We should form some kind of AST representation for the implied + // memcpy in a union copy operation. + if (Field->isUnnamedBitfield() || Field->getParent()->isUnion()) continue; if (Field->isInvalidDecl()) { diff --git a/clang/test/CodeGenCXX/copy-constructor-synthesis-2.cpp b/clang/test/CodeGenCXX/copy-constructor-synthesis-2.cpp index dc9ca23cdf9..4bea685dbf2 100644 --- a/clang/test/CodeGenCXX/copy-constructor-synthesis-2.cpp +++ b/clang/test/CodeGenCXX/copy-constructor-synthesis-2.cpp @@ -1,4 +1,24 @@ -// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -emit-llvm -o - %s | FileCheck %s + +union PR23373 { + PR23373(PR23373&) = default; + PR23373 &operator=(PR23373&) = default; + int n; + float f; +}; +extern PR23373 pr23373_a; + +PR23373 pr23373_b(pr23373_a); +// CHECK-LABEL: define {{.*}} @__cxx_global_var_init( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}} @pr23373_b{{.*}}, {{.*}} @pr23373_a{{.*}}, i64 4, i32 4, i1 false) + +PR23373 pr23373_f() { return pr23373_a; } +// CHECK-LABEL: define {{.*}} @_Z9pr23373_fv( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i64 4, i32 4, i1 false) + +void pr23373_g(PR23373 &a, PR23373 &b) { a = b; } +// CHECK-LABEL: define {{.*}} @_Z9pr23373_g +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i64 4, i32 4, i1 false) struct A { virtual void a(); }; A x(A& y) { return y; } |