Ensure that destructors are called for NRVO'd objects when the

function does not return. Thanks to Eli for pointing out this corner
case.

llvm-svn: 103941
This commit is contained in:
Douglas Gregor 2010-05-17 15:52:46 +00:00
parent f5e8c86424
commit 9154b5dffe
4 changed files with 65 additions and 5 deletions

View File

@ -399,6 +399,7 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
bool IsSimpleConstantInitializer = false;
bool NRVO = false;
llvm::Value *NRVOFlag = 0;
llvm::Value *DeclPtr;
if (Ty->isConstantSizeType()) {
if (!Target.useGlobalsForAutomaticVariables()) {
@ -430,6 +431,21 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
// return slot, so that we can elide the copy when returning this
// variable (C++0x [class.copy]p34).
DeclPtr = ReturnValue;
if (const RecordType *RecordTy = Ty->getAs<RecordType>()) {
if (!cast<CXXRecordDecl>(RecordTy->getDecl())->hasTrivialDestructor()) {
// Create a flag that is used to indicate when the NRVO was applied
// to this variable. Set it to zero to indicate that NRVO was not
// applied.
const llvm::Type *BoolTy = llvm::Type::getInt1Ty(VMContext);
llvm::Value *Zero = llvm::ConstantInt::get(BoolTy, 0);
NRVOFlag = CreateTempAlloca(BoolTy, "nrvo");
Builder.CreateStore(Zero, NRVOFlag);
// Record the NRVO flag for this variable.
NRVOFlags[&D] = NRVOFlag;
}
}
} else {
if (isByRef)
LTy = BuildByRefType(&D);
@ -660,7 +676,8 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
if (const RecordType *RT = DtorTy->getAs<RecordType>())
if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
if (!ClassDecl->hasTrivialDestructor()) {
// Note: We suppress the destructor call when this is an NRVO variable.
// Note: We suppress the destructor call when the corresponding NRVO
// flag has been set.
llvm::Value *Loc = DeclPtr;
if (isByRef)
Loc = Builder.CreateStructGEP(DeclPtr, getByRefValueLLVMField(&D),
@ -693,10 +710,21 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
EmitCXXAggrDestructorCall(D, Array, BaseAddrPtr);
}
} else {
if (!NRVO) {
{
// Normal destruction.
DelayedCleanupBlock Scope(*this);
if (NRVO) {
// If we exited via NRVO, we skip the destructor call.
llvm::BasicBlock *NoNRVO = createBasicBlock("nrvo.unused");
Builder.CreateCondBr(Builder.CreateLoad(NRVOFlag, "nrvo.val"),
Scope.getCleanupExitBlock(),
NoNRVO);
EmitBlock(NoNRVO);
}
// We don't call the destructor along the normal edge if we're
// applying the NRVO.
DelayedCleanupBlock Scope(*this);
EmitCXXDestructorCall(D, Dtor_Complete, /*ForVirtualBase=*/false,
Loc);

View File

@ -612,6 +612,14 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
// Apply the named return value optimization for this return statement,
// which means doing nothing: the appropriate result has already been
// constructed into the NRVO variable.
// If there is an NRVO flag for this variable, set it to 1 into indicate
// that the cleanup code should not destroy the variable.
if (llvm::Value *NRVOFlag = NRVOFlags[S.getNRVOCandidate()]) {
const llvm::Type *BoolTy = llvm::Type::getInt1Ty(VMContext);
llvm::Value *One = llvm::ConstantInt::get(BoolTy, 1);
Builder.CreateStore(One, NRVOFlag);
}
} else if (!ReturnValue) {
// Make sure not to return anything, but evaluate the expression
// for side effects.

View File

@ -107,6 +107,11 @@ public:
bool Exceptions;
bool CatchUndefined;
/// \brief A mapping from NRVO variables to the flags used to indicate
/// when the NRVO has been applied to this variable.
llvm::DenseMap<const VarDecl *, llvm::Value *> NRVOFlags;
public:
/// ObjCEHValueStack - Stack of Objective-C exception values, used for
/// rethrows.

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -emit-llvm -O1 -o - %s | FileCheck %s
// RUN: %clang_cc1 -emit-llvm -fexceptions -o - %s | FileCheck --check-prefix=CHECK-EH %s
// Test code generation for the named return value optimization.
@ -15,9 +15,12 @@ X test0() {
X x;
// CHECK-NOT: call void @_ZN1XD1Ev
// CHECK: ret void
// CHECK-EH: br label
// CHECK-EH: call void @_ZN1XD1Ev
// CHECK-EH: br label
// CHECK-EH: invoke void @_ZN1XD1Ev
// CHECK-EH: ret void
return x;
// CHECK-EH: invoke void @_ZN1XD1Ev
}
// CHECK: define void @_Z5test1b(
@ -63,3 +66,19 @@ X test3(bool B) {
X x;
return x;
}
extern "C" void exit(int) throw();
// CHECK: define void @_Z5test4b
X test4(bool B) {
{
// CHECK: tail call void @_ZN1XC1Ev
X x;
// CHECK: br i1
if (B)
return x;
}
// CHECK: tail call void @_ZN1XD1Ev
// CHECK: tail call void @exit(i32 1)
exit(1);
}