diff options
-rw-r--r-- | include/clang/AST/ExprCXX.h | 11 | ||||
-rw-r--r-- | include/clang/AST/Type.h | 7 | ||||
-rw-r--r-- | lib/AST/ExprCXX.cpp | 4 | ||||
-rw-r--r-- | lib/CodeGen/CGExprCXX.cpp | 159 | ||||
-rw-r--r-- | test/CodeGenCXX/exceptions.cpp | 69 |
5 files changed, 171 insertions, 79 deletions
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index df044ec078..1f9b3b7228 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1080,6 +1080,17 @@ public: TypeSourceInfo *getAllocatedTypeSourceInfo() const { return AllocatedTypeInfo; } + + /// \brief True if the allocation result needs to be null-checked. + /// C++0x [expr.new]p13: + /// If the allocation function returns null, initialization shall + /// not be done, the deallocation function shall not be called, + /// and the value of the new-expression shall be null. + /// An allocation function is not allowed to return null unless it + /// has a non-throwing exception-specification. The '03 rule is + /// identical except that the definition of a non-throwing + /// exception specification is just "is it throw()?". + bool shouldNullCheckAllocation() const; FunctionDecl *getOperatorNew() const { return OperatorNew; } void setOperatorNew(FunctionDecl *D) { OperatorNew = D; } diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index c194e7baa9..713316f146 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2484,6 +2484,13 @@ public: getNumExceptions() == 0; } + /// \brief Returns true if this function type has a non-throwing + /// exception-specification. + bool hasNonThrowingExceptionSpec() const { + // FIXME: this needs to be updated for C++0x. + return hasEmptyExceptionSpec(); + } + using FunctionType::isVariadic; /// \brief Determines whether this function prototype contains a diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 3dac125990..966c1fe6a7 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -100,6 +100,10 @@ void CXXNewExpr::AllocateArgsArray(ASTContext &C, bool isArray, SubExprs = new (C) Stmt*[TotalSize]; } +bool CXXNewExpr::shouldNullCheckAllocation() const { + return getOperatorNew()->getType()-> + castAs<FunctionProtoType>()->hasNonThrowingExceptionSpec(); +} // CXXDeleteExpr QualType CXXDeleteExpr::getDestroyedType() const { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 2b8efa0e39..d0d0f4eb1f 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -920,150 +920,153 @@ static void EnterNewDeleteCleanup(CodeGenFunction &CGF, } llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { - QualType AllocType = E->getAllocatedType(); - if (AllocType->isArrayType()) - while (const ArrayType *AType = getContext().getAsArrayType(AllocType)) - AllocType = AType->getElementType(); + // The element type being allocated. + QualType allocType = getContext().getBaseElementType(E->getAllocatedType()); - FunctionDecl *NewFD = E->getOperatorNew(); - const FunctionProtoType *NewFTy = NewFD->getType()->getAs<FunctionProtoType>(); + // 1. Build a call to the allocation function. + FunctionDecl *allocator = E->getOperatorNew(); + const FunctionProtoType *allocatorType = + allocator->getType()->castAs<FunctionProtoType>(); - CallArgList NewArgs; + CallArgList allocatorArgs; // The allocation size is the first argument. - QualType SizeTy = getContext().getSizeType(); + QualType sizeType = getContext().getSizeType(); - llvm::Value *NumElements = 0; - llvm::Value *AllocSizeWithoutCookie = 0; - llvm::Value *AllocSize = EmitCXXNewAllocSize(getContext(), - *this, E, NumElements, - AllocSizeWithoutCookie); + llvm::Value *numElements = 0; + llvm::Value *allocSizeWithoutCookie = 0; + llvm::Value *allocSize = + EmitCXXNewAllocSize(getContext(), *this, E, numElements, + allocSizeWithoutCookie); - NewArgs.push_back(std::make_pair(RValue::get(AllocSize), SizeTy)); + allocatorArgs.push_back(std::make_pair(RValue::get(allocSize), sizeType)); // Emit the rest of the arguments. // FIXME: Ideally, this should just use EmitCallArgs. - CXXNewExpr::const_arg_iterator NewArg = E->placement_arg_begin(); + CXXNewExpr::const_arg_iterator placementArg = E->placement_arg_begin(); // First, use the types from the function type. // We start at 1 here because the first argument (the allocation size) // has already been emitted. - for (unsigned i = 1, e = NewFTy->getNumArgs(); i != e; ++i, ++NewArg) { - QualType ArgType = NewFTy->getArgType(i); + for (unsigned i = 1, e = allocatorType->getNumArgs(); i != e; + ++i, ++placementArg) { + QualType argType = allocatorType->getArgType(i); - assert(getContext().getCanonicalType(ArgType.getNonReferenceType()). - getTypePtr() == - getContext().getCanonicalType(NewArg->getType()).getTypePtr() && + assert(getContext().hasSameUnqualifiedType(argType.getNonReferenceType(), + placementArg->getType()) && "type mismatch in call argument!"); - NewArgs.push_back(std::make_pair(EmitCallArg(*NewArg, ArgType), - ArgType)); + allocatorArgs.push_back(std::make_pair(EmitCallArg(*placementArg, argType), + argType)); } // Either we've emitted all the call args, or we have a call to a // variadic function. - assert((NewArg == E->placement_arg_end() || NewFTy->isVariadic()) && - "Extra arguments in non-variadic function!"); + assert((placementArg == E->placement_arg_end() || + allocatorType->isVariadic()) && + "Extra arguments to non-variadic function!"); // If we still have any arguments, emit them using the type of the argument. - for (CXXNewExpr::const_arg_iterator NewArgEnd = E->placement_arg_end(); - NewArg != NewArgEnd; ++NewArg) { - QualType ArgType = NewArg->getType(); - NewArgs.push_back(std::make_pair(EmitCallArg(*NewArg, ArgType), - ArgType)); + for (CXXNewExpr::const_arg_iterator placementArgsEnd = E->placement_arg_end(); + placementArg != placementArgsEnd; ++placementArg) { + QualType argType = placementArg->getType(); + allocatorArgs.push_back(std::make_pair(EmitCallArg(*placementArg, argType), + argType)); } - // Emit the call to new. + // Emit the allocation call. RValue RV = - EmitCall(CGM.getTypes().getFunctionInfo(NewArgs, NewFTy), - CGM.GetAddrOfFunction(NewFD), ReturnValueSlot(), NewArgs, NewFD); + EmitCall(CGM.getTypes().getFunctionInfo(allocatorArgs, allocatorType), + CGM.GetAddrOfFunction(allocator), ReturnValueSlot(), + allocatorArgs, allocator); - // If an allocation function is declared with an empty exception specification - // it returns null to indicate failure to allocate storage. [expr.new]p13. - // (We don't need to check for null when there's no new initializer and - // we're allocating a POD type). - bool NullCheckResult = NewFTy->hasEmptyExceptionSpec() && - !(AllocType->isPODType() && !E->hasInitializer()); + // Emit a null check on the allocation result if the allocation + // function is allowed to return null (because it has a non-throwing + // exception spec; for this part, we inline + // CXXNewExpr::shouldNullCheckAllocation()) and we have an + // interesting initializer. + bool nullCheck = allocatorType->hasNonThrowingExceptionSpec() && + !(allocType->isPODType() && !E->hasInitializer()); - llvm::BasicBlock *NullCheckSource = 0; - llvm::BasicBlock *NewNotNull = 0; - llvm::BasicBlock *NewEnd = 0; + llvm::BasicBlock *nullCheckBB = 0; + llvm::BasicBlock *contBB = 0; - llvm::Value *NewPtr = RV.getScalarVal(); - unsigned AS = cast<llvm::PointerType>(NewPtr->getType())->getAddressSpace(); + llvm::Value *allocation = RV.getScalarVal(); + unsigned AS = + cast<llvm::PointerType>(allocation->getType())->getAddressSpace(); // The null-check means that the initializer is conditionally // evaluated. ConditionalEvaluation conditional(*this); - if (NullCheckResult) { - NullCheckSource = Builder.GetInsertBlock(); - NewNotNull = createBasicBlock("new.notnull"); - NewEnd = createBasicBlock("new.end"); + if (nullCheck) { + conditional.begin(*this); - llvm::Value *IsNull = Builder.CreateIsNull(NewPtr, "new.isnull"); - Builder.CreateCondBr(IsNull, NewEnd, NewNotNull); - EmitBlock(NewNotNull); + nullCheckBB = Builder.GetInsertBlock(); + llvm::BasicBlock *notNullBB = createBasicBlock("new.notnull"); + contBB = createBasicBlock("new.cont"); - conditional.begin(*this); + llvm::Value *isNull = Builder.CreateIsNull(allocation, "new.isnull"); + Builder.CreateCondBr(isNull, contBB, notNullBB); + EmitBlock(notNullBB); } - assert((AllocSize == AllocSizeWithoutCookie) == + assert((allocSize == allocSizeWithoutCookie) == CalculateCookiePadding(*this, E).isZero()); - if (AllocSize != AllocSizeWithoutCookie) { + if (allocSize != allocSizeWithoutCookie) { assert(E->isArray()); - NewPtr = CGM.getCXXABI().InitializeArrayCookie(*this, NewPtr, NumElements, - E, AllocType); + allocation = CGM.getCXXABI().InitializeArrayCookie(*this, allocation, + numElements, + E, allocType); } // If there's an operator delete, enter a cleanup to call it if an // exception is thrown. - EHScopeStack::stable_iterator CallOperatorDelete; + EHScopeStack::stable_iterator operatorDeleteCleanup; if (E->getOperatorDelete()) { - EnterNewDeleteCleanup(*this, E, NewPtr, AllocSize, NewArgs); - CallOperatorDelete = EHStack.stable_begin(); + EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocatorArgs); + operatorDeleteCleanup = EHStack.stable_begin(); } - const llvm::Type *ElementPtrTy - = ConvertTypeForMem(AllocType)->getPointerTo(AS); - NewPtr = Builder.CreateBitCast(NewPtr, ElementPtrTy); + const llvm::Type *elementPtrTy + = ConvertTypeForMem(allocType)->getPointerTo(AS); + llvm::Value *result = Builder.CreateBitCast(allocation, elementPtrTy); if (E->isArray()) { - EmitNewInitializer(*this, E, NewPtr, NumElements, AllocSizeWithoutCookie); + EmitNewInitializer(*this, E, result, numElements, allocSizeWithoutCookie); // NewPtr is a pointer to the base element type. If we're // allocating an array of arrays, we'll need to cast back to the // array pointer type. - const llvm::Type *ResultTy = ConvertTypeForMem(E->getType()); - if (NewPtr->getType() != ResultTy) - NewPtr = Builder.CreateBitCast(NewPtr, ResultTy); + const llvm::Type *resultType = ConvertTypeForMem(E->getType()); + if (result->getType() != resultType) + result = Builder.CreateBitCast(result, resultType); } else { - EmitNewInitializer(*this, E, NewPtr, NumElements, AllocSizeWithoutCookie); + EmitNewInitializer(*this, E, result, numElements, allocSizeWithoutCookie); } // Deactivate the 'operator delete' cleanup if we finished // initialization. - if (CallOperatorDelete.isValid()) - DeactivateCleanupBlock(CallOperatorDelete); + if (operatorDeleteCleanup.isValid()) + DeactivateCleanupBlock(operatorDeleteCleanup); - if (NullCheckResult) { + if (nullCheck) { conditional.end(*this); - Builder.CreateBr(NewEnd); - llvm::BasicBlock *NotNullSource = Builder.GetInsertBlock(); - EmitBlock(NewEnd); + llvm::BasicBlock *notNullBB = Builder.GetInsertBlock(); + EmitBlock(contBB); - llvm::PHINode *PHI = Builder.CreatePHI(NewPtr->getType()); + llvm::PHINode *PHI = Builder.CreatePHI(result->getType()); PHI->reserveOperandSpace(2); - PHI->addIncoming(NewPtr, NotNullSource); - PHI->addIncoming(llvm::Constant::getNullValue(NewPtr->getType()), - NullCheckSource); + PHI->addIncoming(result, notNullBB); + PHI->addIncoming(llvm::Constant::getNullValue(result->getType()), + nullCheckBB); - NewPtr = PHI; + result = PHI; } - return NewPtr; + return result; } void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD, diff --git a/test/CodeGenCXX/exceptions.cpp b/test/CodeGenCXX/exceptions.cpp index 5c38f01309..d9b850672c 100644 --- a/test/CodeGenCXX/exceptions.cpp +++ b/test/CodeGenCXX/exceptions.cpp @@ -318,8 +318,75 @@ namespace test7 { ~B(); }; - // Just make sure the result passes verification. B *test() { + // CHECK: define [[B:%.*]]* @_ZN5test74testEv() + // CHECK: [[OUTER_NEW:%.*]] = alloca i1 + // CHECK-NEXT: alloca [[A:%.*]], + // CHECK-NEXT: alloca i8* + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: [[OUTER_A:%.*]] = alloca i1 + // CHECK-NEXT: alloca i8* + // CHECK-NEXT: [[INNER_NEW:%.*]] = alloca i1 + // CHECK-NEXT: alloca [[A]] + // CHECK-NEXT: [[INNER_A:%.*]] = alloca i1 + + // These entry-block stores are to deactivate the delete cleanups. + // CHECK-NEXT: store i1 false, i1* [[INNER_NEW]] + // CHECK-NEXT: store i1 false, i1* [[OUTER_NEW]] + + // Allocate the outer object. + // CHECK-NEXT: [[NEW:%.*]] = call i8* @_ZN5test71BnwEm( + // CHECK-NEXT: icmp eq i8* [[NEW]], null + + // These stores, emitted before the outermost conditional branch, + // deactivate the temporary cleanups. + // CHECK-NEXT: store i1 false, i1* [[OUTER_A]] + // CHECK-NEXT: store i1 false, i1* [[INNER_A]] + // CHECK-NEXT: br i1 + + // We passed the first null check; activate that cleanup and continue. + // CHECK: store i1 true, i1* [[OUTER_NEW]] + // CHECK-NEXT: bitcast + + // Create the first A temporary and activate that cleanup. + // CHECK-NEXT: invoke void @_ZN5test71AC1Ev( + // CHECK: store i1 true, i1* [[OUTER_A]] + + // Allocate the inner object. + // CHECK-NEXT: [[NEW:%.*]] = call i8* @_ZN5test71BnwEm( + // CHECK-NEXT: icmp eq i8* [[NEW]], null + // CHECK-NEXT: br i1 + + // We passed the second null check; save that pointer, activate + // that cleanup, and continue. + // CHECK: store i8* [[NEW]] + // CHECK-NEXT: store i1 true, i1* [[INNER_NEW]] + // CHECK-NEXT: bitcast + + // Build the second A temporary and activate that cleanup. + // CHECK-NEXT: invoke void @_ZN5test71AC1Ev( + // CHECK: store i1 true, i1* [[INNER_A]] + + // Build the inner B object and deactivate the inner delete cleanup. + // CHECK-NEXT: invoke void @_ZN5test71BC1ERKNS_1AEPS0_( + // CHECK: store i1 false, i1* [[INNER_NEW]] + // CHECK: phi + + // Build the outer B object and deactivate the outer delete cleanup. + // CHECK-NEXT: invoke void @_ZN5test71BC1ERKNS_1AEPS0_( + // CHECK: store i1 false, i1* [[OUTER_NEW]] + // CHECK: phi + + // Destroy the inner A object. + // CHECK-NEXT: load i1* [[INNER_A]] + // CHECK-NEXT: br i1 + // CHECK: invoke void @_ZN5test71AD1Ev( + + // Destroy the outer A object. + // CHECK: load i1* [[OUTER_A]] + // CHECK-NEXT: br i1 + // CHECK: invoke void @_ZN5test71AD1Ev( + return new B(A(), new B(A(), 0)); } } |