[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