[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