diff options
author | Fariborz Jahanian <fjahanian@apple.com> | 2012-01-28 18:46:31 +0000 |
---|---|---|
committer | Fariborz Jahanian <fjahanian@apple.com> | 2012-01-28 18:46:31 +0000 |
commit | 326efeb95a3a64c9f20229dd769ff6ff8ac1ac5d (patch) | |
tree | 5a8b5a99bfaa65acd22d636311d08309f0b3f61d | |
parent | 1412816686bd4a7b9d7fe72b14c5c4e62d8d4062 (diff) | |
download | bcm5719-llvm-326efeb95a3a64c9f20229dd769ff6ff8ac1ac5d.tar.gz bcm5719-llvm-326efeb95a3a64c9f20229dd769ff6ff8ac1ac5d.zip |
objc-arc: Perform null check on receiver before sending methods which
consume one or more of their arguments. If not done, this will cause a leak
as method will not consume the argument when receiver is null.
// rdar://10444474
llvm-svn: 149184
-rw-r--r-- | clang/lib/CodeGen/CGObjC.cpp | 20 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGObjCMac.cpp | 75 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 70 | ||||
-rw-r--r-- | clang/test/CodeGenObjC/arc.m | 8 |
4 files changed, 109 insertions, 64 deletions
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 446bef12493..49979f3279d 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -136,7 +136,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, bool isDelegateInit = E->isDelegateInitCall(); const ObjCMethodDecl *method = E->getMethodDecl(); - + // We don't retain the receiver in delegate init calls, and this is // safe because the receiver value is always loaded from 'self', // which we zero out. We don't want to Block_copy block receivers, @@ -191,6 +191,19 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, break; } + // Check to see if receiver must be null checked before method is sent + // to the receiver. + NullReturnState nullReturn; + if (CGM.getLangOptions().ObjCAutoRefCount && method) + for (ObjCMethodDecl::param_const_iterator i = method->param_begin(), + e = method->param_end(); i != e; ++i) { + const ParmVarDecl *ParamDecl = (*i); + if (ParamDecl->hasAttr<NSConsumedAttr>()) { + nullReturn.init(*this, Receiver); + break; + } + } + if (retainSelf) Receiver = EmitARCRetainNonBlock(Receiver); @@ -262,8 +275,8 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, Builder.CreateStore(newSelf, selfAddr); } - - return AdjustRelatedResultType(*this, E, method, result); + RValue rvalue = AdjustRelatedResultType(*this, E, method, result); + return nullReturn.complete(*this, rvalue, ResultType); } namespace { @@ -292,6 +305,7 @@ struct FinishARCDealloc : EHScopeStack::Cleanup { method); } }; + } /// StartObjCMethod - Begin emission of an ObjCMethod. This generates diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index ecf6181b1ce..8bd96382c9e 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -1392,63 +1392,6 @@ public: const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); }; - -/// A helper class for performing the null-initialization of a return -/// value. -struct NullReturnState { - llvm::BasicBlock *NullBB; - llvm::BasicBlock *callBB; - NullReturnState() : NullBB(0), callBB(0) {} - - void init(CodeGenFunction &CGF, llvm::Value *receiver) { - // Make blocks for the null-init and call edges. - NullBB = CGF.createBasicBlock("msgSend.nullinit"); - callBB = CGF.createBasicBlock("msgSend.call"); - - // Check for a null receiver and, if there is one, jump to the - // null-init test. - llvm::Value *isNull = CGF.Builder.CreateIsNull(receiver); - CGF.Builder.CreateCondBr(isNull, NullBB, callBB); - - // Otherwise, start performing the call. - CGF.EmitBlock(callBB); - } - - RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType) { - if (!NullBB) return result; - - // Finish the call path. - llvm::BasicBlock *contBB = CGF.createBasicBlock("msgSend.cont"); - if (CGF.HaveInsertPoint()) CGF.Builder.CreateBr(contBB); - - // Emit the null-init block and perform the null-initialization there. - CGF.EmitBlock(NullBB); - if (!resultType->isAnyComplexType()) { - assert(result.isAggregate() && "null init of non-aggregate result?"); - CGF.EmitNullInitialization(result.getAggregateAddr(), resultType); - // Jump to the continuation block. - CGF.EmitBlock(contBB); - return result; - } - - // _Complex type - // FIXME. Now easy to handle any other scalar type whose result is returned - // in memory due to ABI limitations. - CGF.EmitBlock(contBB); - CodeGenFunction::ComplexPairTy CallCV = result.getComplexVal(); - llvm::Type *MemberType = CallCV.first->getType(); - llvm::Constant *ZeroCV = llvm::Constant::getNullValue(MemberType); - // Create phi instruction for scalar complex value. - llvm::PHINode *PHIReal = CGF.Builder.CreatePHI(MemberType, 2); - PHIReal->addIncoming(ZeroCV, NullBB); - PHIReal->addIncoming(CallCV.first, callBB); - llvm::PHINode *PHIImag = CGF.Builder.CreatePHI(MemberType, 2); - PHIImag->addIncoming(ZeroCV, NullBB); - PHIImag->addIncoming(CallCV.second, callBB); - return RValue::getComplex(PHIReal, PHIImag); - } -}; - } // end anonymous namespace /* *** Helper Functions *** */ @@ -1655,7 +1598,23 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, llvm::Constant *Fn = NULL; if (CGM.ReturnTypeUsesSRet(FnInfo)) { - if (!IsSuper) nullReturn.init(CGF, Arg0); + if (!IsSuper) { + bool nullCheckAlreadyDone = false; + // We have already done this computation once and flag could have been + // passed down. But such cases are extremely rare and we do this lazily, + // instead of absorbing cost of passing down a flag for all cases. + if (CGM.getLangOptions().ObjCAutoRefCount && Method) + for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(), + e = Method->param_end(); i != e; ++i) { + if ((*i)->hasAttr<NSConsumedAttr>()) { + nullCheckAlreadyDone = true; + break; + } + } + if (!nullCheckAlreadyDone) + nullReturn.init(CGF, Arg0); + } + Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper) : ObjCTypes.getSendStretFn(IsSuper); } else if (CGM.ReturnTypeUsesFPRet(ResultType)) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e105f31f0f2..ef81087c3f3 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2627,7 +2627,75 @@ template <> struct DominatingValue<RValue> { } }; +/// A helper class for performing the null-initialization of a return +/// value. +struct NullReturnState { + llvm::BasicBlock *NullBB; + llvm::BasicBlock *callBB; + NullReturnState() : NullBB(0), callBB(0) {} + + void init(CodeGenFunction &CGF, llvm::Value *receiver) { + // Make blocks for the null-init and call edges. + NullBB = CGF.createBasicBlock("msgSend.nullinit"); + callBB = CGF.createBasicBlock("msgSend.call"); + + // Check for a null receiver and, if there is one, jump to the + // null-init test. + llvm::Value *isNull = CGF.Builder.CreateIsNull(receiver); + CGF.Builder.CreateCondBr(isNull, NullBB, callBB); + + // Otherwise, start performing the call. + CGF.EmitBlock(callBB); + } + + RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType) { + if (!NullBB) return result; + + llvm::Value *NullInitPtr = 0; + if (result.isScalar() && !resultType->isVoidType()) { + NullInitPtr = CGF.CreateTempAlloca(result.getScalarVal()->getType()); + CGF.Builder.CreateStore(result.getScalarVal(), NullInitPtr); + } + // Finish the call path. + llvm::BasicBlock *contBB = CGF.createBasicBlock("msgSend.cont"); + if (CGF.HaveInsertPoint()) CGF.Builder.CreateBr(contBB); + + // Emit the null-init block and perform the null-initialization there. + CGF.EmitBlock(NullBB); + if (result.isScalar()) { + if (NullInitPtr) + CGF.EmitNullInitialization(NullInitPtr, resultType); + // Jump to the continuation block. + CGF.EmitBlock(contBB); + return NullInitPtr ? RValue::get(CGF.Builder.CreateLoad(NullInitPtr)) + : result; + } + + if (!resultType->isAnyComplexType()) { + assert(result.isAggregate() && "null init of non-aggregate result?"); + CGF.EmitNullInitialization(result.getAggregateAddr(), resultType); + // Jump to the continuation block. + CGF.EmitBlock(contBB); + return result; + } + + // _Complex type + // FIXME. Now easy to handle any other scalar type whose result is returned + // in memory due to ABI limitations. + CGF.EmitBlock(contBB); + CodeGenFunction::ComplexPairTy CallCV = result.getComplexVal(); + llvm::Type *MemberType = CallCV.first->getType(); + llvm::Constant *ZeroCV = llvm::Constant::getNullValue(MemberType); + // Create phi instruction for scalar complex value. + llvm::PHINode *PHIReal = CGF.Builder.CreatePHI(MemberType, 2); + PHIReal->addIncoming(ZeroCV, NullBB); + PHIReal->addIncoming(CallCV.first, callBB); + llvm::PHINode *PHIImag = CGF.Builder.CreatePHI(MemberType, 2); + PHIImag->addIncoming(ZeroCV, NullBB); + PHIImag->addIncoming(CallCV.second, callBB); + return RValue::getComplex(PHIReal, PHIImag); + } +}; } // end namespace CodeGen } // end namespace clang - #endif diff --git a/clang/test/CodeGenObjC/arc.m b/clang/test/CodeGenObjC/arc.m index 492c1b89da1..8fd1a91cd7d 100644 --- a/clang/test/CodeGenObjC/arc.m +++ b/clang/test/CodeGenObjC/arc.m @@ -1500,12 +1500,16 @@ void test66(void) { // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST66]]* [[T0]] to i8* // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST66]]* -// CHECK-NEXT: [[T4:%.*]] = call i8* @test66_arg() +// CHECK-NEXT: [[THREE:%.*]] = icmp eq [[TEST23:%.*]]* [[T3]], null +// CHECK-NEXT: br i1 [[THREE]], label [[NULINIT:%.*]], label [[MSGCALL:%.*]] +// CHECK: [[T4:%.*]] = call i8* @test66_arg() // CHECK-NEXT: [[T5:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T4]]) // CHECK-NEXT: [[T6:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES // CHECK-NEXT: [[T7:%.*]] = bitcast [[TEST66]]* [[T3]] to i8* // CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* [[T7]], i8* [[T6]], i8* [[T5]]) -// CHECK-NEXT: [[T8:%.*]] = bitcast [[TEST66]]* [[T3]] to i8* +// CHECK-NEXT: br label [[MSGCONT:%.*]] +// CHECK: br label [[MSGCONT:%.*]] +// CHECK: [[T8:%.*]] = bitcast [[TEST66]]* [[T3]] to i8* // CHECK-NEXT: call void @objc_release(i8* [[T8]]) // CHECK-NEXT: ret void |