diff options
-rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 2 | ||||
-rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 4 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGCXX.cpp | 121 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGCall.cpp | 12 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 136 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 3 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.cpp | 22 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.h | 1 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenTypes.h | 2 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/constructors.cpp | 94 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/default-arguments.cpp | 6 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/destructors.cpp | 46 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/virtual-destructor-calls.cpp | 25 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/vtable-pointer-initialization.cpp | 11 |
14 files changed, 425 insertions, 60 deletions
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 0978c6da9d6..264c8959f69 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -710,7 +710,7 @@ public: CXXConstructorDecl *getDefaultConstructor(ASTContext &Context); /// getDestructor - Returns the destructor decl for this class. - CXXDestructorDecl *getDestructor(ASTContext &Context); + CXXDestructorDecl *getDestructor(ASTContext &Context) const; /// isLocalClass - If the class is a local class [class.local], returns /// the enclosing function declaration. diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index b0569d68015..8d2a379f811 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -543,14 +543,14 @@ CXXRecordDecl::getDefaultConstructor(ASTContext &Context) { return 0; } -CXXDestructorDecl *CXXRecordDecl::getDestructor(ASTContext &Context) { +CXXDestructorDecl *CXXRecordDecl::getDestructor(ASTContext &Context) const { QualType ClassType = Context.getTypeDeclType(this); DeclarationName Name = Context.DeclarationNames.getCXXDestructorName( Context.getCanonicalType(ClassType)); - DeclContext::lookup_iterator I, E; + DeclContext::lookup_const_iterator I, E; llvm::tie(I, E) = lookup(Name); assert(I != E && "Did not find a destructor!"); diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp index e84b68104bd..01648ae0747 100644 --- a/clang/lib/CodeGen/CGCXX.cpp +++ b/clang/lib/CodeGen/CGCXX.cpp @@ -27,24 +27,88 @@ using namespace clang; using namespace CodeGen; -/// Try to emit a definition as a global alias for another definition. -bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, - GlobalDecl TargetDecl) { +/// Determines whether the given function has a trivial body that does +/// not require any specific codegen. +static bool HasTrivialBody(const FunctionDecl *FD) { + Stmt *S = FD->getBody(); + if (!S) + return true; + if (isa<CompoundStmt>(S) && cast<CompoundStmt>(S)->body_empty()) + return true; + return false; +} + +/// Try to emit a base destructor as an alias to its primary +/// base-class destructor. +bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) { if (!getCodeGenOpts().CXXCtorDtorAliases) return true; - // Find the referrent. - llvm::GlobalValue *Ref = cast<llvm::GlobalValue>(GetAddrOfGlobal(TargetDecl)); + // If the destructor doesn't have a trivial body, we have to emit it + // separately. + if (!HasTrivialBody(D)) + return true; - // Look for an existing entry. - const char *MangledName = getMangledName(AliasDecl); - llvm::GlobalValue *&Entry = GlobalDeclMap[MangledName]; - if (Entry) { - assert(Entry->isDeclaration() && "definition already exists for alias"); - assert(Entry->getType() == Ref->getType() && - "declaration exists with different type"); + const CXXRecordDecl *Class = D->getParent(); + + // If we need to manipulate a VTT parameter, give up. + if (Class->getNumVBases()) { + // Extra Credit: passing extra parameters is perfectly safe + // in many calling conventions, so only bail out if the ctor's + // calling convention is nonstandard. + return true; } + // If any fields have a non-trivial destructor, we have to emit it + // separately. + for (CXXRecordDecl::field_iterator I = Class->field_begin(), + E = Class->field_end(); I != E; ++I) + if (const RecordType *RT = (*I)->getType()->getAs<RecordType>()) + if (!cast<CXXRecordDecl>(RT->getDecl())->hasTrivialDestructor()) + return true; + + // Try to find a unique base class with a non-trivial destructor. + const CXXRecordDecl *UniqueBase = 0; + for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), + E = Class->bases_end(); I != E; ++I) { + + // We're in the base destructor, so skip virtual bases. + if (I->isVirtual()) continue; + + // Skip base classes with trivial destructors. + const CXXRecordDecl *Base + = cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl()); + if (Base->hasTrivialDestructor()) continue; + + // If we've already found a base class with a non-trivial + // destructor, give up. + if (UniqueBase) return true; + UniqueBase = Base; + } + + // If we didn't find any bases with a non-trivial destructor, then + // the base destructor is actually effectively trivial, which can + // happen if it was needlessly user-defined or if there are virtual + // bases with non-trivial destructors. + if (!UniqueBase) + return true; + + // If the base is at a non-zero offset, give up. + const ASTRecordLayout &ClassLayout = Context.getASTRecordLayout(Class); + if (ClassLayout.getBaseClassOffset(UniqueBase) != 0) + return true; + + const CXXDestructorDecl *BaseD = UniqueBase->getDestructor(getContext()); + return TryEmitDefinitionAsAlias(GlobalDecl(D, Dtor_Base), + GlobalDecl(BaseD, Dtor_Base)); +} + +/// Try to emit a definition as a global alias for another definition. +bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, + GlobalDecl TargetDecl) { + if (!getCodeGenOpts().CXXCtorDtorAliases) + return true; + // The alias will use the linkage of the referrent. If we can't // support aliases with that linkage, fail. llvm::GlobalValue::LinkageTypes Linkage @@ -72,11 +136,32 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, return true; } + // Derive the type for the alias. + const llvm::PointerType *AliasType + = getTypes().GetFunctionType(AliasDecl)->getPointerTo(); + + // Look for an existing entry. + const char *MangledName = getMangledName(AliasDecl); + llvm::GlobalValue *&Entry = GlobalDeclMap[MangledName]; + if (Entry) { + assert(Entry->isDeclaration() && "definition already exists for alias"); + assert(Entry->getType() == AliasType && + "declaration exists with different type"); + } + + // Find the referrent. Some aliases might require a bitcast, in + // which case the caller is responsible for ensuring the soundness + // of these semantics. + llvm::GlobalValue *Ref = cast<llvm::GlobalValue>(GetAddrOfGlobal(TargetDecl)); + llvm::Constant *Aliasee = Ref; + if (Ref->getType() != AliasType) + Aliasee = llvm::ConstantExpr::getBitCast(Ref, AliasType); + // Create the alias with no name. llvm::GlobalAlias *Alias = - new llvm::GlobalAlias(Ref->getType(), Linkage, "", Ref, &getModule()); + new llvm::GlobalAlias(AliasType, Linkage, "", Aliasee, &getModule()); - // Switch any previous uses to the alias and continue. + // Switch any previous uses to the alias and kill the previous decl. if (Entry) { Entry->replaceAllUsesWith(Alias); Entry->eraseFromParent(); @@ -90,7 +175,6 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, return false; } - void CodeGenModule::EmitCXXConstructors(const CXXConstructorDecl *D) { // The constructor used for constructing this as a complete class; // constucts the virtual bases, then calls the base constructor. @@ -169,6 +253,13 @@ void CodeGenModule::EmitCXXDestructor(const CXXDestructorDecl *D, GlobalDecl(D, Dtor_Base))) return; + // The base destructor is equivalent to the base destructor of its + // base class if there is exactly one non-virtual base class with a + // non-trivial destructor, there are no fields with a non-trivial + // destructor, and the body of the destructor is trivial. + if (Type == Dtor_Base && !TryEmitBaseDestructorAsAlias(D)) + return; + llvm::Function *Fn = cast<llvm::Function>(GetAddrOfCXXDestructor(D, Type)); CodeGenFunction(*this).GenerateCode(GlobalDecl(D, Type), Fn); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index b064c125ad0..1cd899318a9 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -416,6 +416,18 @@ bool CodeGenModule::ReturnTypeUsesSret(const CGFunctionInfo &FI) { return FI.getReturnInfo().isIndirect(); } +const llvm::FunctionType *CodeGenTypes::GetFunctionType(GlobalDecl GD) { + const CGFunctionInfo &FI = getFunctionInfo(GD); + + // For definition purposes, don't consider a K&R function variadic. + bool Variadic = false; + if (const FunctionProtoType *FPT = + cast<FunctionDecl>(GD.getDecl())->getType()->getAs<FunctionProtoType>()) + Variadic = FPT->isVariadic(); + + return GetFunctionType(FI, Variadic); +} + const llvm::FunctionType * CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool IsVariadic) { std::vector<const llvm::Type*> ArgTys; diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index d30d2186870..99c6dfd7ebc 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -891,31 +891,80 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, } } +/// Checks whether the given constructor is a valid subject for the +/// complete-to-base constructor delegation optimization, i.e. +/// emitting the complete constructor as a simple call to the base +/// constructor. +static bool IsConstructorDelegationValid(const CXXConstructorDecl *Ctor) { + + // Currently we disable the optimization for classes with virtual + // bases because (1) the addresses of parameter variables need to be + // consistent across all initializers but (2) the delegate function + // call necessarily creates a second copy of the parameter variable. + // + // The limiting example (purely theoretical AFAIK): + // struct A { A(int &c) { c++; } }; + // struct B : virtual A { + // B(int count) : A(count) { printf("%d\n", count); } + // }; + // ...although even this example could in principle be emitted as a + // delegation since the address of the parameter doesn't escape. + if (Ctor->getParent()->getNumVBases()) { + // TODO: white-list trivial vbase initializers. This case wouldn't + // be subject to the restrictions below. + + // TODO: white-list cases where: + // - there are no non-reference parameters to the constructor + // - the initializers don't access any non-reference parameters + // - the initializers don't take the address of non-reference + // parameters + // - etc. + // If we ever add any of the above cases, remember that: + // - function-try-blocks will always blacklist this optimization + // - we need to perform the constructor prologue and cleanup in + // EmitConstructorBody. + + return false; + } + + // We also disable the optimization for variadic functions because + // it's impossible to "re-pass" varargs. + if (Ctor->getType()->getAs<FunctionProtoType>()->isVariadic()) + return false; + + return true; +} + /// EmitConstructorBody - Emits the body of the current constructor. void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) { const CXXConstructorDecl *Ctor = cast<CXXConstructorDecl>(CurGD.getDecl()); CXXCtorType CtorType = CurGD.getCtorType(); + // Before we go any further, try the complete->base constructor + // delegation optimization. + if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor)) { + EmitDelegateCXXConstructorCall(Ctor, Ctor_Base, Args); + return; + } + Stmt *Body = Ctor->getBody(); - // Some of the optimizations we want to do can't be done with - // function try blocks. + // Enter the function-try-block before the constructor prologue if + // applicable. CXXTryStmtInfo TryInfo; - bool isTryBody = (Body && isa<CXXTryStmt>(Body)); - if (isTryBody) + bool IsTryBody = (Body && isa<CXXTryStmt>(Body)); + + if (IsTryBody) TryInfo = EnterCXXTryStmt(*cast<CXXTryStmt>(Body)); unsigned CleanupStackSize = CleanupEntries.size(); - // Emit the constructor prologue, i.e. the base and member initializers. - - // TODO: for non-variadic complete-object constructors without a - // function try block for a body, we can get away with just emitting - // the vbase initializers, then calling the base constructor. + // Emit the constructor prologue, i.e. the base and member + // initializers. EmitCtorPrologue(Ctor, CtorType); // Emit the body of the statement. - if (isTryBody) + if (IsTryBody) EmitStmt(cast<CXXTryStmt>(Body)->getTryBlock()); else if (Body) EmitStmt(Body); @@ -933,7 +982,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) { // constructed. EmitCleanupBlocks(CleanupStackSize); - if (isTryBody) + if (IsTryBody) ExitCXXTryStmt(*cast<CXXTryStmt>(Body), TryInfo); } @@ -1406,6 +1455,71 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, EmitCXXMemberCall(D, Callee, ReturnValueSlot(), This, VTT, ArgBeg, ArgEnd); } +void +CodeGenFunction::EmitDelegateCXXConstructorCall(const CXXConstructorDecl *Ctor, + CXXCtorType CtorType, + const FunctionArgList &Args) { + CallArgList DelegateArgs; + + FunctionArgList::const_iterator I = Args.begin(), E = Args.end(); + assert(I != E && "no parameters to constructor"); + + // this + DelegateArgs.push_back(std::make_pair(RValue::get(LoadCXXThis()), + I->second)); + ++I; + + // vtt + if (llvm::Value *VTT = GetVTTParameter(*this, GlobalDecl(Ctor, CtorType))) { + QualType VoidPP = getContext().getPointerType(getContext().VoidPtrTy); + DelegateArgs.push_back(std::make_pair(RValue::get(VTT), VoidPP)); + + if (CGVtableInfo::needsVTTParameter(CurGD)) { + assert(I != E && "cannot skip vtt parameter, already done with args"); + assert(I->second == VoidPP && "skipping parameter not of vtt type"); + ++I; + } + } + + // Explicit arguments. + for (; I != E; ++I) { + + const VarDecl *Param = I->first; + QualType ArgType = Param->getType(); // because we're passing it to itself + + // StartFunction converted the ABI-lowered parameter(s) into a + // local alloca. We need to turn that into an r-value suitable + // for EmitCall. + llvm::Value *Local = GetAddrOfLocalVar(Param); + RValue Arg; + + // For the most part, we just need to load the alloca, except: + // 1) aggregate r-values are actually pointers to temporaries, and + // 2) references to aggregates are pointers directly to the aggregate. + // I don't know why references to non-aggregates are different here. + if (ArgType->isReferenceType()) { + const ReferenceType *RefType = ArgType->getAs<ReferenceType>(); + if (hasAggregateLLVMType(RefType->getPointeeType())) + Arg = RValue::getAggregate(Local); + else + // Locals which are references to scalars are represented + // with allocas holding the pointer. + Arg = RValue::get(Builder.CreateLoad(Local)); + } else { + if (hasAggregateLLVMType(ArgType)) + Arg = RValue::getAggregate(Local); + else + Arg = RValue::get(EmitLoadOfScalar(Local, false, ArgType)); + } + + DelegateArgs.push_back(std::make_pair(Arg, ArgType)); + } + + EmitCall(CGM.getTypes().getFunctionInfo(Ctor, CtorType), + CGM.GetAddrOfCXXConstructor(Ctor, CtorType), + ReturnValueSlot(), DelegateArgs, Ctor); +} + void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD, CXXDtorType Type, llvm::Value *This) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 1c5cd67fa9d..c4f0a9fcfff 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -789,6 +789,9 @@ public: const CXXRecordDecl *BaseClassDecl, QualType Ty); + void EmitDelegateCXXConstructorCall(const CXXConstructorDecl *Ctor, + CXXCtorType CtorType, + const FunctionArgList &Args); void EmitCXXConstructorCall(const CXXConstructorDecl *D, CXXCtorType Type, llvm::Value *This, CallExpr::const_arg_iterator ArgBeg, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 41575e41e53..f819382a936 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1195,28 +1195,8 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old, void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD) { - const llvm::FunctionType *Ty; const FunctionDecl *D = cast<FunctionDecl>(GD.getDecl()); - - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { - bool isVariadic = D->getType()->getAs<FunctionProtoType>()->isVariadic(); - - Ty = getTypes().GetFunctionType(getTypes().getFunctionInfo(MD), isVariadic); - } else { - Ty = cast<llvm::FunctionType>(getTypes().ConvertType(D->getType())); - - // As a special case, make sure that definitions of K&R function - // "type foo()" aren't declared as varargs (which forces the backend - // to do unnecessary work). - if (D->getType()->isFunctionNoProtoType()) { - assert(Ty->isVarArg() && "Didn't lower type as expected"); - // Due to stret, the lowered function could have arguments. - // Just create the same type as was lowered by ConvertType - // but strip off the varargs bit. - std::vector<const llvm::Type*> Args(Ty->param_begin(), Ty->param_end()); - Ty = llvm::FunctionType::get(Ty->getReturnType(), Args, false); - } - } + const llvm::FunctionType *Ty = getTypes().GetFunctionType(GD); // Get or create the prototype for the function. llvm::Constant *Entry = GetAddrOfFunction(GD, Ty); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index a5e1d9f12b3..ac8332647b7 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -485,6 +485,7 @@ private: // C++ related functions. bool TryEmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target); + bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D); void EmitNamespace(const NamespaceDecl *D); void EmitLinkageSpec(const LinkageSpecDecl *D); diff --git a/clang/lib/CodeGen/CodeGenTypes.h b/clang/lib/CodeGen/CodeGenTypes.h index 87ba0bcfba1..7ce96f48f4e 100644 --- a/clang/lib/CodeGen/CodeGenTypes.h +++ b/clang/lib/CodeGen/CodeGenTypes.h @@ -168,6 +168,8 @@ public: const llvm::FunctionType *GetFunctionType(const CGFunctionInfo &Info, bool IsVariadic); + const llvm::FunctionType *GetFunctionType(GlobalDecl GD); + /// GetFunctionTypeForVtable - Get the LLVM function type for use in a vtable, /// given a CXXMethodDecl. If the method to has an incomplete return type, diff --git a/clang/test/CodeGenCXX/constructors.cpp b/clang/test/CodeGenCXX/constructors.cpp new file mode 100644 index 00000000000..2c95c91e111 --- /dev/null +++ b/clang/test/CodeGenCXX/constructors.cpp @@ -0,0 +1,94 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s + +struct Member { int x; Member(); Member(int); Member(const Member &); }; +struct VBase { int x; VBase(); VBase(int); VBase(const VBase &); }; + +struct ValueClass { + ValueClass(int x, int y) : x(x), y(y) {} + int x; + int y; +}; // subject to ABI trickery + + + +/* Test basic functionality. */ +class A { + A(struct Undeclared &); + A(ValueClass); + Member mem; +}; + +A::A(struct Undeclared &ref) : mem(0) {} + +// Check that delegation works. +// CHECK: define void @_ZN1AC1ER10Undeclared( +// CHECK: call void @_ZN1AC2ER10Undeclared( + +// CHECK: define void @_ZN1AC2ER10Undeclared( +// CHECK: call void @_ZN6MemberC1Ei( + +A::A(ValueClass v) : mem(v.y - v.x) {} + +// CHECK: define void @_ZN1AC1E10ValueClass( +// CHECK: call void @_ZN1AC2E10ValueClass( + +// CHECK: define void @_ZN1AC2E10ValueClass( +// CHECK: call void @_ZN6MemberC1Ei( + + +/* Test that things work for inheritance. */ +struct B : A { + B(struct Undeclared &); + Member mem; +}; + +B::B(struct Undeclared &ref) : A(ref), mem(1) {} + +// CHECK: define void @_ZN1BC1ER10Undeclared( +// CHECK: call void @_ZN1BC2ER10Undeclared( + +// CHECK: define void @_ZN1BC2ER10Undeclared( +// CHECK: call void @_ZN1AC2ER10Undeclared( +// CHECK: call void @_ZN6MemberC1Ei( + + + +/* Test that the delegation optimization is disabled for classes with + virtual bases (for now). This is necessary because a vbase + initializer could access one of the parameter variables by + reference. That's a solvable problem, but let's not solve it right + now. */ +struct C : virtual A { + C(int); + Member mem; +}; +C::C(int x) : A(ValueClass(x, x+1)), mem(x * x) {} + +// CHECK: define void @_ZN1CC1Ei( +// CHECK: call void @_ZN10ValueClassC1Eii( +// CHECK: call void @_ZN1AC2E10ValueClass( +// CHECK: call void @_ZN6MemberC1Ei( + +// CHECK: define void @_ZN1CC2Ei( +// CHECK: call void @_ZN6MemberC1Ei( + + + +/* Test that the delegation optimization is disabled for varargs + constructors. */ +struct D : A { + D(int, ...); + Member mem; +}; + +D::D(int x, ...) : A(ValueClass(x, x+1)), mem(x*x) {} + +// CHECK: define void @_ZN1DC1Eiz( +// CHECK: call void @_ZN10ValueClassC1Eii( +// CHECK: call void @_ZN1AC2E10ValueClass( +// CHECK: call void @_ZN6MemberC1Ei( + +// CHECK: define void @_ZN1DC2Eiz( +// CHECK: call void @_ZN10ValueClassC1Eii( +// CHECK: call void @_ZN1AC2E10ValueClass( +// CHECK: call void @_ZN6MemberC1Ei( diff --git a/clang/test/CodeGenCXX/default-arguments.cpp b/clang/test/CodeGenCXX/default-arguments.cpp index 282e5d0d504..2ed1567697c 100644 --- a/clang/test/CodeGenCXX/default-arguments.cpp +++ b/clang/test/CodeGenCXX/default-arguments.cpp @@ -43,11 +43,7 @@ struct C { }; // CHECK: define void @_ZN1CC1Ev( -// CHECK: call void @_ZN2A1C1Ev( -// CHECK: call void @_ZN2A2C1Ev( -// CHECK: call void @_ZN1BC1ERK2A1RK2A2( -// CHECK: call void @_ZN2A2D1Ev -// CHECK: call void @_ZN2A1D1Ev +// CHECK: call void @_ZN1CC2Ev( // CHECK: define void @_ZN1CC2Ev( // CHECK: call void @_ZN2A1C1Ev( diff --git a/clang/test/CodeGenCXX/destructors.cpp b/clang/test/CodeGenCXX/destructors.cpp index f06661a7447..accd1b34986 100644 --- a/clang/test/CodeGenCXX/destructors.cpp +++ b/clang/test/CodeGenCXX/destructors.cpp @@ -1,4 +1,11 @@ // RUN: %clang_cc1 %s -emit-llvm -o - -mconstructor-aliases | FileCheck %s + +// CHECK: @_ZN5test01AD1Ev = alias {{.*}} @_ZN5test01AD2Ev +// CHECK: @_ZN5test11MD2Ev = alias {{.*}} @_ZN5test11AD2Ev +// CHECK: @_ZN5test11ND2Ev = alias {{.*}} @_ZN5test11AD2Ev +// CHECK: @_ZN5test11OD2Ev = alias {{.*}} @_ZN5test11AD2Ev +// CHECK: @_ZN5test11SD2Ev = alias bitcast {{.*}} @_ZN5test11AD2Ev + struct A { int a; @@ -60,7 +67,7 @@ namespace test0 { // The function-try-block won't suppress -mconstructor-aliases here. A::~A() try { } catch (int i) {} -// CHECK: @_ZN5test01AD1Ev = alias {{.*}} @_ZN5test01AD2Ev +// complete destructor alias tested above // CHECK: define void @_ZN5test01AD2Ev // CHECK: invoke void @_ZN5test06MemberD1Ev @@ -89,3 +96,40 @@ namespace test0 { // CHECK: invoke void @_ZN5test04BaseD2Ev // CHECK: unwind label [[BASE_UNWIND:%[a-zA-Z0-9.]+]] } + +// Test base-class aliasing. +namespace test1 { + struct A { ~A(); char ***m; }; // non-trivial destructor + struct B { ~B(); }; // non-trivial destructor + struct Empty { }; // trivial destructor, empty + struct NonEmpty { int x; }; // trivial destructor, non-empty + + struct M : A { ~M(); }; + M::~M() {} // alias tested above + + struct N : A, Empty { ~N(); }; + N::~N() {} // alias tested above + + struct O : Empty, A { ~O(); }; + O::~O() {} // alias tested above + + struct P : NonEmpty, A { ~P(); }; + P::~P() {} // CHECK: define void @_ZN5test11PD2Ev + + struct Q : A, B { ~Q(); }; + Q::~Q() {} // CHECK: define void @_ZN5test11QD2Ev + + struct R : A { ~R(); }; + R::~R() { A a; } // CHECK: define void @_ZN5test11RD2Ev + + struct S : A { ~S(); int x; }; + S::~S() {} // alias tested above + + struct T : A { ~T(); B x; }; + T::~T() {} // CHECK: define void @_ZN5test11TD2Ev + + // The VTT parameter prevents this. We could still make this work + // for calling conventions that are safe against extra parameters. + struct U : A, virtual B { ~U(); }; + U::~U() {} // CHECK: define void @_ZN5test11UD2Ev +} diff --git a/clang/test/CodeGenCXX/virtual-destructor-calls.cpp b/clang/test/CodeGenCXX/virtual-destructor-calls.cpp index 91fd598d4c9..c5b9262a067 100644 --- a/clang/test/CodeGenCXX/virtual-destructor-calls.cpp +++ b/clang/test/CodeGenCXX/virtual-destructor-calls.cpp @@ -1,16 +1,25 @@ // RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin10 -mconstructor-aliases | FileCheck %s +struct Member { + ~Member(); +}; + struct A { virtual ~A(); }; struct B : A { + Member m; virtual ~B(); }; // Complete dtor: just an alias because there are no virtual bases. // CHECK: @_ZN1BD1Ev = alias {{.*}} @_ZN1BD2Ev +// (aliases from C) +// CHECK: @_ZN1CD1Ev = alias {{.*}} @_ZN1CD2Ev +// CHECK: @_ZN1CD2Ev = alias bitcast {{.*}} @_ZN1BD2Ev + // Deleting dtor: defers to the complete dtor. // CHECK: define void @_ZN1BD0Ev // CHECK: call void @_ZN1BD1Ev @@ -18,6 +27,22 @@ struct B : A { // Base dtor: actually calls A's base dtor. // CHECK: define void @_ZN1BD2Ev +// CHECK: call void @_ZN6MemberD1Ev // CHECK: call void @_ZN1AD2Ev B::~B() { } + +struct C : B { + ~C(); +}; + +C::~C() { } + +// Complete dtor: just an alias (checked above). + +// Deleting dtor: defers to the complete dtor. +// CHECK: define void @_ZN1CD0Ev +// CHECK: call void @_ZN1CD1Ev +// CHECK: call void @_ZdlPv + +// Base dtor: just an alias to B's base dtor. diff --git a/clang/test/CodeGenCXX/vtable-pointer-initialization.cpp b/clang/test/CodeGenCXX/vtable-pointer-initialization.cpp index ebe531529b8..75620ab8e62 100644 --- a/clang/test/CodeGenCXX/vtable-pointer-initialization.cpp +++ b/clang/test/CodeGenCXX/vtable-pointer-initialization.cpp @@ -42,13 +42,16 @@ struct B : Base { void f() { B b; } // CHECK: define linkonce_odr void @_ZN1BC1Ev( -// CHECK: call void @_ZN4BaseC2Ev( -// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1B, i64 0, i64 2) -// CHECK: call void @_ZN5FieldC1Ev -// CHECK: ret void +// CHECK: call void @_ZN1BC2Ev( // CHECK: define linkonce_odr void @_ZN1BD1Ev( // CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1B, i64 0, i64 2) // CHECK: call void @_ZN5FieldD1Ev( // CHECK: call void @_ZN4BaseD2Ev( // CHECK: ret void + +// CHECK: define linkonce_odr void @_ZN1BC2Ev( +// CHECK: call void @_ZN4BaseC2Ev( +// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1B, i64 0, i64 2) +// CHECK: call void @_ZN5FieldC1Ev +// CHECK: ret void |