diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 0954e20050d7..9d9562014848 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1128,10 +1128,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { value = CGF.EmitARCRetainAutoreleasedReturnValue(value); return CGF.EmitObjCConsumeObject(E->getType(), value); } - case CK_ARCExtendBlockObject: { - llvm::Value *value = CGF.EmitARCRetainScalarExpr(E); - return CGF.EmitObjCConsumeObject(E->getType(), value); - } + case CK_ARCExtendBlockObject: + return CGF.EmitARCExtendBlockObject(E); case CK_FloatingRealToComplex: case CK_FloatingComplexCast: diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 72e0c3052403..344c1ee18047 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -1658,7 +1658,7 @@ static void emitARCCopyOperation(CodeGenFunction &CGF, /// call i8* @objc_retainBlock(i8* %value) llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) { if (type->isBlockPointerType()) - return EmitARCRetainBlock(value); + return EmitARCRetainBlock(value, /*mandatory*/ false); else return EmitARCRetainNonBlock(value); } @@ -1673,10 +1673,32 @@ llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) { /// Retain the given block, with _Block_copy semantics. /// call i8* @objc_retainBlock(i8* %value) -llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value) { - return emitARCValueOperation(*this, value, - CGM.getARCEntrypoints().objc_retainBlock, - "objc_retainBlock"); +/// +/// \param mandatory - If false, emit the call with metadata +/// indicating that it's okay for the optimizer to eliminate this call +/// if it can prove that the block never escapes except down the stack. +llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value, + bool mandatory) { + llvm::Value *result + = emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainBlock, + "objc_retainBlock"); + + // If the copy isn't mandatory, add !clang.arc.copy_on_escape to + // tell the optimizer that it doesn't need to do this copy if the + // block doesn't escape, where being passed as an argument doesn't + // count as escaping. + if (!mandatory && isa(result)) { + llvm::CallInst *call + = cast(result->stripPointerCasts()); + assert(call->getCalledValue() == CGM.getARCEntrypoints().objc_retainBlock); + + SmallVector args; + call->setMetadata("clang.arc.copy_on_escape", + llvm::MDNode::get(Builder.getContext(), args)); + } + + return result; } /// Retain the given object which is the result of a function call. @@ -1857,7 +1879,7 @@ llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type, llvm::Type *origType = value->getType(); value = Builder.CreateBitCast(value, Int8PtrTy); - value = EmitARCRetainBlock(value); + value = EmitARCRetainBlock(value, /*mandatory*/ true); value = EmitARCAutorelease(value); return Builder.CreateBitCast(value, origType); } @@ -2300,7 +2322,7 @@ tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { } // Retain the object as a block, then cast down. - result = CGF.EmitARCRetainBlock(result); + result = CGF.EmitARCRetainBlock(result, /*mandatory*/ true); if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); return TryEmitResult(result, true); } @@ -2386,6 +2408,24 @@ CodeGenFunction::EmitARCRetainAutoreleaseScalarExpr(const Expr *e) { return value; } +llvm::Value *CodeGenFunction::EmitARCExtendBlockObject(const Expr *e) { + llvm::Value *result; + bool doRetain; + + if (shouldEmitSeparateBlockRetain(e)) { + result = EmitScalarExpr(e); + doRetain = true; + } else { + TryEmitResult subresult = tryEmitARCRetainScalarExpr(*this, e); + result = subresult.getPointer(); + doRetain = !subresult.getInt(); + } + + if (doRetain) + result = EmitARCRetainBlock(result, /*mandatory*/ true); + return EmitObjCConsumeObject(e->getType(), result); +} + llvm::Value *CodeGenFunction::EmitObjCThrowOperand(const Expr *expr) { // In ARC, retain and autorelease the expression. if (getLangOptions().ObjCAutoRefCount) { @@ -2423,7 +2463,7 @@ CodeGenFunction::EmitARCStoreStrong(const BinaryOperator *e, // type, then we need to emit the block-retain immediately in case // it invalidates the l-value. if (!hasImmediateRetain && e->getType()->isBlockPointerType()) { - value = EmitARCRetainBlock(value); + value = EmitARCRetainBlock(value, /*mandatory*/ false); hasImmediateRetain = true; } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 0f120923d612..9a18d3367b6a 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2129,7 +2129,7 @@ public: bool ignored); llvm::Value *EmitARCRetain(QualType type, llvm::Value *value); llvm::Value *EmitARCRetainNonBlock(llvm::Value *value); - llvm::Value *EmitARCRetainBlock(llvm::Value *value); + llvm::Value *EmitARCRetainBlock(llvm::Value *value, bool mandatory); void EmitARCRelease(llvm::Value *value, bool precise); llvm::Value *EmitARCAutorelease(llvm::Value *value); llvm::Value *EmitARCAutoreleaseReturnValue(llvm::Value *value); @@ -2147,6 +2147,7 @@ public: llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr); llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr); + llvm::Value *EmitARCExtendBlockObject(const Expr *expr); llvm::Value *EmitARCRetainScalarExpr(const Expr *expr); llvm::Value *EmitARCRetainAutoreleaseScalarExpr(const Expr *expr); diff --git a/clang/test/CodeGenObjC/arc-block-copy-escape.m b/clang/test/CodeGenObjC/arc-block-copy-escape.m new file mode 100644 index 000000000000..15c0d1d0f496 --- /dev/null +++ b/clang/test/CodeGenObjC/arc-block-copy-escape.m @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fobjc-arc -fblocks -emit-llvm %s -o - | FileCheck %s + +typedef void (^block_t)(void); +void use_block(block_t); +void use_int(int); + +// rdar://problem/10211676 + +void test0(int i) { + block_t block = ^{ use_int(i); }; + // CHECK: define void @test0( + // CHECK: call i8* @objc_retainBlock(i8* {{%.*}}) nounwind, !clang.arc.copy_on_escape + // CHECK: ret void +} + +void test1(int i) { + id block = ^{ use_int(i); }; + // CHECK: define void @test1( + // CHECK: call i8* @objc_retainBlock(i8* {{%.*}}) nounwind + // CHECK-NOT: !clang.arc.copy_on_escape + // CHECK: ret void +}