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:
parent
f5e8c86424
commit
9154b5dffe
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue