[cfe-commits] r127151 - in /cfe/trunk: include/clang/AST/ExprCXX.h include/clang/AST/Type.h lib/AST/ExprCXX.cpp lib/CodeGen/CGExprCXX.cpp test/CodeGenCXX/exceptions.cpp
John McCall
rjmccall at apple.com
Sun Mar 6 19:12:35 PST 2011
Author: rjmccall
Date: Sun Mar 6 21:12:35 2011
New Revision: 127151
URL: http://llvm.org/viewvc/llvm-project?rev=127151&view=rev
Log:
The conditional needs to be pushed before the branch. Make the test less
trivial to check this. Adjust for style.
Modified:
cfe/trunk/include/clang/AST/ExprCXX.h
cfe/trunk/include/clang/AST/Type.h
cfe/trunk/lib/AST/ExprCXX.cpp
cfe/trunk/lib/CodeGen/CGExprCXX.cpp
cfe/trunk/test/CodeGenCXX/exceptions.cpp
Modified: cfe/trunk/include/clang/AST/ExprCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=127151&r1=127150&r2=127151&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ExprCXX.h (original)
+++ cfe/trunk/include/clang/AST/ExprCXX.h Sun Mar 6 21:12:35 2011
@@ -1080,6 +1080,17 @@
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; }
Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=127151&r1=127150&r2=127151&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Sun Mar 6 21:12:35 2011
@@ -2484,6 +2484,13 @@
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
Modified: cfe/trunk/lib/AST/ExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=127151&r1=127150&r2=127151&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprCXX.cpp (original)
+++ cfe/trunk/lib/AST/ExprCXX.cpp Sun Mar 6 21:12:35 2011
@@ -100,6 +100,10 @@
SubExprs = new (C) Stmt*[TotalSize];
}
+bool CXXNewExpr::shouldNullCheckAllocation() const {
+ return getOperatorNew()->getType()->
+ castAs<FunctionProtoType>()->hasNonThrowingExceptionSpec();
+}
// CXXDeleteExpr
QualType CXXDeleteExpr::getDestroyedType() const {
Modified: cfe/trunk/lib/CodeGen/CGExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprCXX.cpp?rev=127151&r1=127150&r2=127151&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprCXX.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp Sun Mar 6 21:12:35 2011
@@ -920,150 +920,153 @@
}
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);
-
- // 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());
-
- llvm::BasicBlock *NullCheckSource = 0;
- llvm::BasicBlock *NewNotNull = 0;
- llvm::BasicBlock *NewEnd = 0;
-
- llvm::Value *NewPtr = RV.getScalarVal();
- unsigned AS = cast<llvm::PointerType>(NewPtr->getType())->getAddressSpace();
+ EmitCall(CGM.getTypes().getFunctionInfo(allocatorArgs, allocatorType),
+ CGM.GetAddrOfFunction(allocator), ReturnValueSlot(),
+ allocatorArgs, allocator);
+
+ // 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 *nullCheckBB = 0;
+ llvm::BasicBlock *contBB = 0;
+
+ 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");
-
- llvm::Value *IsNull = Builder.CreateIsNull(NewPtr, "new.isnull");
- Builder.CreateCondBr(IsNull, NewEnd, NewNotNull);
- EmitBlock(NewNotNull);
-
+ if (nullCheck) {
conditional.begin(*this);
+
+ nullCheckBB = Builder.GetInsertBlock();
+ llvm::BasicBlock *notNullBB = createBasicBlock("new.notnull");
+ contBB = createBasicBlock("new.cont");
+
+ 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,
Modified: cfe/trunk/test/CodeGenCXX/exceptions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/exceptions.cpp?rev=127151&r1=127150&r2=127151&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/exceptions.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/exceptions.cpp Sun Mar 6 21:12:35 2011
@@ -318,8 +318,75 @@
~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));
}
}
More information about the cfe-commits
mailing list