]> granicus.if.org Git - clang/commitdiff
Perform two more constructor/destructor code-size optimizations:
authorJohn McCall <rjmccall@apple.com>
Tue, 23 Feb 2010 00:48:20 +0000 (00:48 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 23 Feb 2010 00:48:20 +0000 (00:48 +0000)
1) emit base destructors as aliases to their unique base class destructors
under some careful conditions.  This is enabled for the same targets that can
support complete-to-base aliases, i.e. not darwin.

2) Emit non-variadic complete constructors for classes with no virtual bases
as calls to the base constructor.  This is enabled on all targets and in
theory can trigger in situations that the alias optimization can't (mostly
involving virtual bases, mostly not yet supported).

These are bundled together because I didn't think it worthwhile to split them,
not because they really need to be.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96842 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGCall.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/CodeGenModule.h
lib/CodeGen/CodeGenTypes.h
test/CodeGenCXX/constructors.cpp [new file with mode: 0644]
test/CodeGenCXX/default-arguments.cpp
test/CodeGenCXX/destructors.cpp
test/CodeGenCXX/virtual-destructor-calls.cpp
test/CodeGenCXX/vtable-pointer-initialization.cpp

index 0978c6da9d66a36fa59b3c09c681f9fef1b310a1..264c8959f69fd151793cf10ad788ab806bd55662 100644 (file)
@@ -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.
index b0569d68015f8d9961aa77e03acfa5ece06b0643..8d2a379f811eef4b198cdc287fb7d9d49833d746 100644 (file)
@@ -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!");
 
index e84b68104bd0f566ac2f4726bf3d660bd1edce9f..01648ae074796df8c192b7dea94cc440de772186 100644 (file)
 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);
index b064c125ad005d641842126e28ce15c101b65aec..1cd899318a92c453eaf5b5f5f901b6942097e8ea 100644 (file)
@@ -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;
index d30d2186870cec4f2ef2f9fa81c3f0eb7d25b671..99c6dfd7ebc434eb8326a3198d3c55238d256f74 100644 (file)
@@ -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) {
index 1c5cd67fa9dc57c5a0235427b5d6e25c90961e04..c4f0a9fcfffa2f95a87dd0c18d1086efa5952567 100644 (file)
@@ -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,
index 41575e41e53cdb135b6104edef50b9ddf99c7c21..f819382a9366abbdc1782c15a062156610dfe90b 100644 (file)
@@ -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);
index a5e1d9f12b33e71823bbfeb784958cb521cfd199..ac8332647b77a57a7031d3c39690b427fff0b4c3 100644 (file)
@@ -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);
index 87ba0bcfba1d00271fd2b895f5c916f0f3b36aea..7ce96f48f4e8bfb7b6a1230bf85f5a39dd088675 100644 (file)
@@ -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/test/CodeGenCXX/constructors.cpp b/test/CodeGenCXX/constructors.cpp
new file mode 100644 (file)
index 0000000..2c95c91
--- /dev/null
@@ -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(
index 282e5d0d5042248f7dad5ab4b1a6c39986d5c6e4..2ed1567697cac5e8d339b98efc725c1ca7c40295 100644 (file)
@@ -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(
index f06661a7447eb51f8cabf975e0e91c06df86eae9..accd1b349867544fba2a313af8984a793fe58a0f 100644 (file)
@@ -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
+}
index 91fd598d4c922076ecd06321b4cddf29f49f56cf..c5b9262a067830a79fd9479efcc0a0fb519cc26a 100644 (file)
@@ -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.
index ebe531529b81aa8ac45dfdbcd46f46bc0c29d277..75620ab8e62a56e4da363a9d7b7b0f70a51ad1cb 100644 (file)
@@ -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