[cfe-commits] r96670 - in /cfe/trunk: lib/CodeGen/CGClass.cpp lib/CodeGen/CGException.cpp lib/CodeGen/CodeGenFunction.cpp lib/CodeGen/CodeGenFunction.h test/CodeGenCXX/destructors.cpp

John McCall rjmccall at apple.com
Fri Feb 19 01:25:03 PST 2010


Author: rjmccall
Date: Fri Feb 19 03:25:03 2010
New Revision: 96670

URL: http://llvm.org/viewvc/llvm-project?rev=96670&view=rev
Log:
More refactoring around constructor/destructor code generation.
Fix some bugs with function-try-blocks and simplify normal try-block
code generation.

This implementation excludes a deleting destructor's call to
operator delete() from the function-try-block, which I believe
is correct but which I can't find straightforward support for at
a moment's glance.


Modified:
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGException.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/test/CodeGenCXX/destructors.cpp

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=96670&r1=96669&r2=96670&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Fri Feb 19 03:25:03 2010
@@ -14,6 +14,7 @@
 #include "CodeGenFunction.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/RecordLayout.h"
+#include "clang/AST/StmtCXX.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -599,46 +600,6 @@
            Callee, ReturnValueSlot(), CallArgs, MD);
 }
 
-/// Synthesizes an implicit function body.  Since these only arise in
-/// C++, we only do them in C++.
-void CodeGenFunction::SynthesizeImplicitFunctionBody(GlobalDecl GD,
-                                                     llvm::Function *Fn,
-                                               const FunctionArgList &Args) {
-  const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
-
-  // FIXME: this should become isImplicitlyDefined() once we properly
-  // support that for C++0x.
-  assert(FD->isImplicit() && "Cannot synthesize a non-implicit function");
-
-  if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FD)) {
-    assert(!CD->isTrivial() && "shouldn't need to synthesize a trivial ctor");
-
-    if (CD->isDefaultConstructor()) {
-      // Sema generates base and member initializers as for this, so
-      // the ctor prologue is good enough here.
-      return;
-    } else {
-      assert(CD->isCopyConstructor());
-      return SynthesizeCXXCopyConstructor(CD, GD.getCtorType(), Fn, Args);
-    }
-  }
-
-  if (isa<CXXDestructorDecl>(FD)) {
-    // The dtor epilogue does everything we'd need to do here.
-    return;
-  }
-
-  const CXXMethodDecl *MD = cast<CXXMethodDecl>(FD);
-
-  // FIXME: in C++0x we might have user-declared copy assignment operators
-  // coexisting with implicitly-defined ones.
-  assert(MD->isCopyAssignment() &&
-         !MD->getParent()->hasUserDeclaredCopyAssignment() &&
-         "Cannot synthesize a method that is not an implicitly-defined "
-         "copy constructor");
-  SynthesizeCXXCopyAssignment(MD, Fn, Args);
-}
-
 /// SynthesizeCXXCopyConstructor - This routine implicitly defines body of a
 /// copy constructor, in accordance with section 12.8 (p7 and p8) of C++03
 /// The implicitly-defined copy constructor for class X performs a memberwise
@@ -655,10 +616,8 @@
 /// implicitly-defined copy constructor
 
 void 
-CodeGenFunction::SynthesizeCXXCopyConstructor(const CXXConstructorDecl *Ctor,
-                                              CXXCtorType Type,
-                                              llvm::Function *Fn,
-                                              const FunctionArgList &Args) {
+CodeGenFunction::SynthesizeCXXCopyConstructor(const FunctionArgList &Args) {
+  const CXXConstructorDecl *Ctor = cast<CXXConstructorDecl>(CurGD.getDecl());
   const CXXRecordDecl *ClassDecl = Ctor->getParent();
   assert(!ClassDecl->hasUserDeclaredCopyConstructor() &&
       "SynthesizeCXXCopyConstructor - copy constructor has definition already");
@@ -754,10 +713,8 @@
 ///
 ///   if the subobject is of scalar type, the built-in assignment operator is
 ///   used.
-void CodeGenFunction::SynthesizeCXXCopyAssignment(const CXXMethodDecl *CD,
-                                                  llvm::Function *Fn,
-                                                  const FunctionArgList &Args) {
-
+void CodeGenFunction::SynthesizeCXXCopyAssignment(const FunctionArgList &Args) {
+  const CXXMethodDecl *CD = cast<CXXMethodDecl>(CurGD.getDecl());
   const CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(CD->getDeclContext());
   assert(!ClassDecl->hasUserDeclaredCopyAssignment() &&
          "SynthesizeCXXCopyAssignment - copy assignment has user declaration");
@@ -934,6 +891,52 @@
   }
 }
 
+/// EmitConstructorBody - Emits the body of the current constructor.
+void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
+  const CXXConstructorDecl *Ctor = cast<CXXConstructorDecl>(CurGD.getDecl());
+  CXXCtorType CtorType = CurGD.getCtorType();
+
+  Stmt *Body = Ctor->getBody();
+
+  // Some of the optimizations we want to do can't be done with
+  // function try blocks.
+  CXXTryStmtInfo TryInfo;
+  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.
+  EmitCtorPrologue(Ctor, CtorType);
+
+  // Emit the body of the statement.
+  if (isTryBody)
+    EmitStmt(cast<CXXTryStmt>(Body)->getTryBlock());
+  else if (Body)
+    EmitStmt(Body);
+  else {
+    assert(Ctor->isImplicit() && "bodyless ctor not implicit");
+    if (!Ctor->isDefaultConstructor()) {
+      assert(Ctor->isCopyConstructor());
+      SynthesizeCXXCopyConstructor(Args);
+    }
+  }
+
+  // Emit any cleanup blocks associated with the member or base
+  // initializers, which includes (along the exceptional path) the
+  // destructors for those members and bases that were fully
+  // constructed.
+  EmitCleanupBlocks(CleanupStackSize);
+
+  if (isTryBody)
+    ExitCXXTryStmt(*cast<CXXTryStmt>(Body), TryInfo);
+}
+
 /// EmitCtorPrologue - This routine generates necessary code to initialize
 /// base classes and non-static data members belonging to this constructor.
 void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
@@ -968,6 +971,84 @@
   }
 }
 
+/// EmitDestructorBody - Emits the body of the current destructor.
+void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
+  const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl());
+  CXXDtorType DtorType = CurGD.getDtorType();
+
+  Stmt *Body = Dtor->getBody();
+
+  // If the body is a function-try-block, enter the try before
+  // anything else --- unless we're in a deleting destructor, in which
+  // case we're just going to call the complete destructor and then
+  // call operator delete() on the way out.
+  CXXTryStmtInfo TryInfo;
+  bool isTryBody = (DtorType != Dtor_Deleting &&
+                    Body && isa<CXXTryStmt>(Body));
+  if (isTryBody)
+    TryInfo = EnterCXXTryStmt(*cast<CXXTryStmt>(Body));
+
+  llvm::BasicBlock *DtorEpilogue = createBasicBlock("dtor.epilogue");
+  PushCleanupBlock(DtorEpilogue);
+
+  bool SkipBody = false; // should get jump-threaded
+
+  // If this is the deleting variant, just invoke the complete
+  // variant, then call the appropriate operator delete() on the way
+  // out.
+  if (DtorType == Dtor_Deleting) {
+    EmitCXXDestructorCall(Dtor, Dtor_Complete, LoadCXXThis());
+    SkipBody = true;
+
+  // If this is the complete variant, just invoke the base variant;
+  // the epilogue will destruct the virtual bases.  But we can't do
+  // this optimization if the body is a function-try-block, because
+  // we'd introduce *two* handler blocks.
+  } else if (!isTryBody && DtorType == Dtor_Complete) {
+    EmitCXXDestructorCall(Dtor, Dtor_Base, LoadCXXThis());
+    SkipBody = true;
+      
+  // Otherwise, we're in the base variant, so we need to ensure the
+  // vtable ptrs are right before emitting the body.
+  } else {
+    InitializeVtablePtrs(Dtor->getParent());
+  }
+
+  // Emit the body of the statement.
+  if (SkipBody)
+    (void) 0;
+  else if (isTryBody)
+    EmitStmt(cast<CXXTryStmt>(Body)->getTryBlock());
+  else if (Body)
+    EmitStmt(Body);
+  else {
+    assert(Dtor->isImplicit() && "bodyless dtor not implicit");
+    // nothing to do besides what's in the epilogue
+  }
+
+  // Jump to the cleanup block.
+  CleanupBlockInfo Info = PopCleanupBlock();
+  assert(Info.CleanupBlock == DtorEpilogue && "Block mismatch!");
+  EmitBlock(DtorEpilogue);
+
+  // Emit the destructor epilogue now.  If this is a complete
+  // destructor with a function-try-block, perform the base epilogue
+  // as well.
+  if (isTryBody && DtorType == Dtor_Complete)
+    EmitDtorEpilogue(Dtor, Dtor_Base);
+  EmitDtorEpilogue(Dtor, DtorType);
+
+  // Link up the cleanup information.
+  if (Info.SwitchBlock)
+    EmitBlock(Info.SwitchBlock);
+  if (Info.EndBlock)
+    EmitBlock(Info.EndBlock);
+
+  // Exit the try if applicable.
+  if (isTryBody)
+    ExitCXXTryStmt(*cast<CXXTryStmt>(Body), TryInfo);
+}
+
 /// EmitDtorEpilogue - Emit all code that comes at the end of class's
 /// destructor. This is to call destructors on members and base classes
 /// in reverse order of their construction.

Modified: cfe/trunk/lib/CodeGen/CGException.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGException.cpp?rev=96670&r1=96669&r2=96670&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CGException.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGException.cpp Fri Feb 19 03:25:03 2010
@@ -427,6 +427,26 @@
 }
 
 void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) {
+  CXXTryStmtInfo Info = EnterCXXTryStmt(S);
+  EmitStmt(S.getTryBlock());
+  ExitCXXTryStmt(S, Info);
+}
+
+CodeGenFunction::CXXTryStmtInfo
+CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S) {
+  CXXTryStmtInfo Info;
+  Info.SavedLandingPad = getInvokeDest();
+  Info.HandlerBlock = createBasicBlock("try.handler");
+  Info.FinallyBlock = createBasicBlock("finally");
+
+  PushCleanupBlock(Info.FinallyBlock);
+  setInvokeDest(Info.HandlerBlock);
+
+  return Info;
+}
+
+void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S,
+                                     CXXTryStmtInfo TryInfo) {
   // Pointer to the personality function
   llvm::Constant *Personality =
     CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getInt32Ty
@@ -439,54 +459,12 @@
   llvm::Value *llvm_eh_selector =
     CGM.getIntrinsic(llvm::Intrinsic::eh_selector);
 
-  llvm::BasicBlock *PrevLandingPad = getInvokeDest();
-  llvm::BasicBlock *TryHandler = createBasicBlock("try.handler");
-  llvm::BasicBlock *FinallyBlock = createBasicBlock("finally");
+  llvm::BasicBlock *PrevLandingPad = TryInfo.SavedLandingPad;
+  llvm::BasicBlock *TryHandler = TryInfo.HandlerBlock;
+  llvm::BasicBlock *FinallyBlock = TryInfo.FinallyBlock;
   llvm::BasicBlock *FinallyRethrow = createBasicBlock("finally.throw");
   llvm::BasicBlock *FinallyEnd = createBasicBlock("finally.end");
 
-  // Push an EH context entry, used for handling rethrows.
-  PushCleanupBlock(FinallyBlock);
-
-  // Emit the statements in the try {} block
-  setInvokeDest(TryHandler);
-
-  // FIXME: We should not have to do this here.  The AST should have the member
-  // initializers under the CXXTryStmt's TryBlock.
-  if (OuterTryBlock == &S) {
-    GlobalDecl GD = CurGD;
-    const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
-
-    if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FD)) {
-      size_t OldCleanupStackSize = CleanupEntries.size();
-      EmitCtorPrologue(CD, CurGD.getCtorType());
-      EmitStmt(S.getTryBlock());
-
-      // If any of the member initializers are temporaries bound to references
-      // make sure to emit their destructors.
-      EmitCleanupBlocks(OldCleanupStackSize);
-    } else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(FD)) {
-      llvm::BasicBlock *DtorEpilogue  = createBasicBlock("dtor.epilogue");
-      PushCleanupBlock(DtorEpilogue);
-
-      InitializeVtablePtrs(DD->getParent());
-      EmitStmt(S.getTryBlock());
-
-      CleanupBlockInfo Info = PopCleanupBlock();
-
-      assert(Info.CleanupBlock == DtorEpilogue && "Block mismatch!");
-      EmitBlock(DtorEpilogue);
-      EmitDtorEpilogue(DD, GD.getDtorType());
-
-      if (Info.SwitchBlock)
-        EmitBlock(Info.SwitchBlock);
-      if (Info.EndBlock)
-        EmitBlock(Info.EndBlock);
-    } else
-      EmitStmt(S.getTryBlock());
-  } else
-    EmitStmt(S.getTryBlock());
-
   // Jump to end if there is no exception
   EmitBranchThroughCleanup(FinallyEnd);
 

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=96670&r1=96669&r2=96670&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Fri Feb 19 03:25:03 2010
@@ -241,80 +241,16 @@
   }
 }
 
-void CodeGenFunction::GenerateBody(GlobalDecl GD, llvm::Function *Fn, 
-                                   FunctionArgList &Args) {
-  const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
+void CodeGenFunction::EmitFunctionBody(FunctionArgList &Args) {
+  const FunctionDecl *FD = cast<FunctionDecl>(CurGD.getDecl());
 
   Stmt *Body = FD->getBody();
-  assert((Body || FD->isImplicit()) && "non-implicit function def has no body");
-
-  bool SkipBody = false; // should get jump-threaded
-
-  // Emit special ctor/dtor prologues.
-  if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FD)) {
-    // Emit the constructor prologue, i.e. the base and member initializers.
-    EmitCtorPrologue(CD, GD.getCtorType());
-
-    // TODO: for complete, non-varargs variants, we can get away with
-    // just emitting the vbase initializers, then calling the base
-    // constructor.
-
-  } else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(FD)) {
-    // In all cases, if there's an exception in the body (or delegate)
-    // we'll still need to run the epilogue.
-    llvm::BasicBlock *DtorEpilogue = createBasicBlock("dtor.epilogue");
-    PushCleanupBlock(DtorEpilogue);
-
-    // If this is the deleting variant, invoke the complete variant;
-    // the epilogue will call the appropriate operator delete().
-    if (GD.getDtorType() == Dtor_Deleting) {
-      EmitCXXDestructorCall(DD, Dtor_Complete, LoadCXXThis());
-      SkipBody = true;
-
-    // If this is the complete variant, just invoke the base variant;
-    // the epilogue will destruct the virtual bases.
-    } else if (GD.getDtorType() == Dtor_Complete) {
-      EmitCXXDestructorCall(DD, Dtor_Base, LoadCXXThis());
-      SkipBody = true;
-
-    // Otherwise, we're in the base variant, so we need to ensure the
-    // vtable ptrs are right before emitting the body.
-    } else {
-      InitializeVtablePtrs(DD->getParent());
-    }
-  }
-
-  // Emit the body of the function.
-  if (SkipBody) {
-    // skipped
-  } else if (!Body) {
-    SynthesizeImplicitFunctionBody(GD, Fn, Args);
-  } else {
-    if (isa<CXXTryStmt>(Body))
-      OuterTryBlock = cast<CXXTryStmt>(Body);
+  if (Body)
     EmitStmt(Body);
-  }
-
-  // Emit special ctor/dtor epilogues.
-  if (isa<CXXConstructorDecl>(FD)) {
-    // Be sure to emit any cleanup blocks associated with the member
-    // or base initializers, which includes (along the exceptional
-    // path) the destructors for those members and bases that were
-    // fully constructed.
-    EmitCleanupBlocks(0);
-
-  } else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(FD)) {
-    // Funnel the previously-pushed cleanup block into the epilogue.
-    CleanupBlockInfo Info = PopCleanupBlock();
-    EmitBlock(Info.CleanupBlock);
-
-    EmitDtorEpilogue(DD, GD.getDtorType());
-
-    // Go ahead and link in the switch and end blocks.
-    if (Info.SwitchBlock)
-      EmitBlock(Info.SwitchBlock);
-    if (Info.EndBlock)
-      EmitBlock(Info.EndBlock);
+  else {
+    assert(FD->isImplicit() && "non-implicit function def has no body");
+    assert(FD->isCopyAssignment() && "implicit function not copy assignment");
+    SynthesizeCXXCopyAssignment(Args);
   }
 }
 
@@ -328,7 +264,6 @@
   FunctionArgList Args;
 
   CurGD = GD;
-  OuterTryBlock = 0;
   if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
     if (MD->isInstance()) {
       // Create the implicit 'this' decl.
@@ -368,7 +303,12 @@
   StartFunction(GD, FD->getResultType(), Fn, Args, BodyRange.getBegin());
 
   // Generate the body of the function.
-  GenerateBody(GD, Fn, Args);
+  if (isa<CXXDestructorDecl>(FD))
+    EmitDestructorBody(Args);
+  else if (isa<CXXConstructorDecl>(FD))
+    EmitConstructorBody(Args);
+  else
+    EmitFunctionBody(Args);
 
   // Emit the standard function epilogue.
   FinishFunction(BodyRange.getEnd());

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=96670&r1=96669&r2=96670&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Fri Feb 19 03:25:03 2010
@@ -91,9 +91,6 @@
 
   /// CurGD - The GlobalDecl for the current function being compiled.
   GlobalDecl CurGD;
-  /// OuterTryBlock - This is the address of the outter most try block, 0
-  /// otherwise.
-  const Stmt *OuterTryBlock;
 
   /// ReturnBlock - Unified return block.
   llvm::BasicBlock *ReturnBlock;
@@ -494,7 +491,9 @@
                      const FunctionArgList &Args,
                      SourceLocation StartLoc);
 
-  void GenerateBody(GlobalDecl GD, llvm::Function *Fn,  FunctionArgList &Args);
+  void EmitConstructorBody(FunctionArgList &Args);
+  void EmitDestructorBody(FunctionArgList &Args);
+  void EmitFunctionBody(FunctionArgList &Args);
 
   /// EmitReturnBlock - Emit the unified return block, trying to avoid its
   /// emission when possible.
@@ -529,18 +528,8 @@
                                      llvm::Value *ThisPtr,
                                      uint64_t Offset);
 
-  void SynthesizeCXXCopyConstructor(const CXXConstructorDecl *Ctor,
-                                    CXXCtorType Type,
-                                    llvm::Function *Fn,
-                                    const FunctionArgList &Args);
-
-  void SynthesizeCXXCopyAssignment(const CXXMethodDecl *CD,
-                                   llvm::Function *Fn,
-                                   const FunctionArgList &Args);
-
-  void SynthesizeImplicitFunctionBody(GlobalDecl GD,
-                                      llvm::Function *Fn,
-                                      const FunctionArgList &Args);
+  void SynthesizeCXXCopyConstructor(const FunctionArgList &Args);
+  void SynthesizeCXXCopyAssignment(const FunctionArgList &Args);
 
   /// EmitDtorEpilogue - Emit all code that comes at the end of class's
   /// destructor. This is to call destructors on members and base classes in
@@ -927,6 +916,14 @@
   void EmitObjCAtThrowStmt(const ObjCAtThrowStmt &S);
   void EmitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt &S);
 
+  struct CXXTryStmtInfo {
+    llvm::BasicBlock *SavedLandingPad;
+    llvm::BasicBlock *HandlerBlock;
+    llvm::BasicBlock *FinallyBlock;
+  };
+  CXXTryStmtInfo EnterCXXTryStmt(const CXXTryStmt &S);
+  void ExitCXXTryStmt(const CXXTryStmt &S, CXXTryStmtInfo Info);
+
   void EmitCXXTryStmt(const CXXTryStmt &S);
   
   //===--------------------------------------------------------------------===//

Modified: cfe/trunk/test/CodeGenCXX/destructors.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/destructors.cpp?rev=96670&r1=96669&r2=96670&view=diff

==============================================================================
--- cfe/trunk/test/CodeGenCXX/destructors.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/destructors.cpp Fri Feb 19 03:25:03 2010
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o -
+// RUN: %clang_cc1 %s -emit-llvm -o - -mconstructor-aliases | FileCheck %s
 struct A {
   int a;
   
@@ -42,3 +42,50 @@
   
   B::~B()  {}
 }
+
+// FIXME: there's a known problem in the codegen here where, if one
+// destructor throws, the remaining destructors aren't run.  Fix it,
+// then make this code check for it.
+namespace test0 {
+  void foo();
+  struct VBase { ~VBase(); };
+  struct Base { ~Base(); };
+  struct Member { ~Member(); };
+
+  struct A : Base {
+    Member M;
+    ~A();
+  };
+
+  // The function-try-block won't suppress -mconstructor-aliases here.
+  A::~A() try { } catch (int i) {}
+
+// CHECK: @_ZN5test01AD1Ev = alias {{.*}} @_ZN5test01AD2Ev
+
+// CHECK: define void @_ZN5test01AD2Ev
+// CHECK: invoke void @_ZN5test06MemberD1Ev
+// CHECK:   unwind label [[MEM_UNWIND:%[a-zA-Z0-9.]+]]
+// CHECK: invoke void @_ZN5test04BaseD2Ev
+// CHECK:   unwind label [[BASE_UNWIND:%[a-zA-Z0-9.]+]]
+
+  struct B : Base, virtual VBase {
+    Member M;
+    ~B();
+  };
+  B::~B() try { } catch (int i) {}
+  // It will suppress the delegation optimization here, though.
+
+// CHECK: define void @_ZN5test01BD1Ev
+// CHECK: invoke void @_ZN5test06MemberD1Ev
+// CHECK:   unwind label [[MEM_UNWIND:%[a-zA-Z0-9.]+]]
+// CHECK: invoke void @_ZN5test04BaseD2Ev
+// CHECK:   unwind label [[BASE_UNWIND:%[a-zA-Z0-9.]+]]
+// CHECK: invoke void @_ZN5test05VBaseD2Ev
+// CHECK:   unwind label [[VBASE_UNWIND:%[a-zA-Z0-9.]+]]
+
+// CHECK: define void @_ZN5test01BD2Ev
+// CHECK: invoke void @_ZN5test06MemberD1Ev
+// CHECK:   unwind label [[MEM_UNWIND:%[a-zA-Z0-9.]+]]
+// CHECK: invoke void @_ZN5test04BaseD2Ev
+// CHECK:   unwind label [[BASE_UNWIND:%[a-zA-Z0-9.]+]]
+}





More information about the cfe-commits mailing list