diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index ab24c8779df26b..47c282f0a63dda 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1664,6 +1664,9 @@ class ParmVarDecl : public VarDecl { return ParmVarDeclBits.IsObjCMethodParam; } + /// Determines whether this parameter is destroyed in the callee function. + bool isDestroyedInCallee() const; + unsigned getFunctionScopeDepth() const { if (ParmVarDeclBits.IsObjCMethodParam) return 0; return ParmVarDeclBits.ScopeDepthOrObjCQuals; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index f0c925f9cdf902..3cea3c23b527be 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2738,6 +2738,17 @@ SourceRange ParmVarDecl::getSourceRange() const { return DeclaratorDecl::getSourceRange(); } +bool ParmVarDecl::isDestroyedInCallee() const { + if (hasAttr()) + return true; + + auto *RT = getType()->getAs(); + if (RT && RT->getDecl()->isParamDestroyedInCallee()) + return true; + + return false; +} + Expr *ParmVarDecl::getDefaultArg() { assert(!hasUnparsedDefaultArg() && "Default argument is not yet parsed!"); assert(!hasUninstantiatedDefaultArg() && diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index dff86744698d43..465d2c5449d550 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -1765,6 +1765,24 @@ struct NullReturnState { assert(RV.isScalar() && "NullReturnState::complete - arg not on object"); CGF.EmitARCRelease(RV.getScalarVal(), ARCImpreciseLifetime); + } else { + QualType QT = ParamDecl->getType(); + auto *RT = QT->getAs(); + if (RT && RT->getDecl()->isParamDestroyedInCallee()) { + RValue RV = I->getRValue(CGF); + QualType::DestructionKind DtorKind = QT.isDestructedType(); + switch (DtorKind) { + case QualType::DK_cxx_destructor: + CGF.destroyCXXObject(CGF, RV.getAggregateAddress(), QT); + break; + case QualType::DK_nontrivial_c_struct: + CGF.destroyNonTrivialCStruct(CGF, RV.getAggregateAddress(), QT); + break; + default: + llvm_unreachable("unexpected dtor kind"); + break; + } + } } } } @@ -2241,7 +2259,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, // Emit a null-check if there's a consumed argument other than the receiver. if (!RequiresNullCheck && CGM.getLangOpts().ObjCAutoRefCount && Method) { for (const auto *ParamDecl : Method->parameters()) { - if (ParamDecl->hasAttr()) { + if (ParamDecl->isDestroyedInCallee()) { RequiresNullCheck = true; break; } @@ -7350,7 +7368,7 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF, bool requiresnullCheck = false; if (CGM.getLangOpts().ObjCAutoRefCount && method) for (const auto *ParamDecl : method->parameters()) { - if (ParamDecl->hasAttr()) { + if (ParamDecl->isDestroyedInCallee()) { if (!nullReturn.NullBB) nullReturn.init(CGF, arg0); requiresnullCheck = true; diff --git a/clang/test/CodeGenObjC/objc-dispatch-null-check.m b/clang/test/CodeGenObjC/objc-dispatch-null-check.m new file mode 100644 index 00000000000000..0c43955db2fa9b --- /dev/null +++ b/clang/test/CodeGenObjC/objc-dispatch-null-check.m @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-dispatch-method=non-legacy -fobjc-arc -emit-llvm -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* } + +typedef struct { + id x; +} Strong; + +Strong getStrong(void); + +@interface I0 +- (void)passStrong:(Strong)a; +@end + +// CHECK-LABEL: define void @test0( +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8 +// CHECK: %[[CALL:.*]] = call i8* @getStrong() +// CHECK-NEXT: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i8* %[[CALL]], i8** %[[COERCE_DIVE]], align 8 + +// CHECK: %[[MSGSEND_FN:.*]] = load i8*, i8** +// CHECK: %[[V5:.*]] = bitcast i8* %[[MSGSEND_FN]] to void (i8*, i8*, i8*)* +// CHECK: %[[COERCE_DIVE1:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0 +// CHECK: %[[V6:.*]] = load i8*, i8** %[[COERCE_DIVE1]], align 8 +// CHECK: call void %[[V5]]({{.*}}, i8* %[[V6]]) +// CHECK: br + +// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONG]]* %[[AGG_TMP]] to i8** +// CHECK: call void @__destructor_8_s0(i8** %[[V7]]) +// CHECK: br + +void test0(I0 *a) { + [a passStrong:getStrong()]; +} diff --git a/clang/test/CodeGenObjC/strong-in-c-struct.m b/clang/test/CodeGenObjC/strong-in-c-struct.m index 4e098c352ab56a..d9e503feafd288 100644 --- a/clang/test/CodeGenObjC/strong-in-c-struct.m +++ b/clang/test/CodeGenObjC/strong-in-c-struct.m @@ -91,6 +91,7 @@ @interface C - (StrongSmall)getStrongSmall; +- (void)m:(StrongSmall)s; + (StrongSmall)getStrongSmallClass; @end @@ -944,4 +945,21 @@ id test_assignment1(void) { calleeStrongSmall(g2 = g1); } +// CHECK-LABEL: define void @test_null_reveiver( +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: br i1 + +// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP]] to [2 x i64]* +// CHECK: %[[V8:.*]] = load [2 x i64], [2 x i64]* %[[V7]], align 8 +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void ({{.*}}, [2 x i64] %[[V8]]) +// CHECK: br + +// CHECK: %[[V9:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V9]]) #4 +// CHECK: br + +void test_null_reveiver(C *c) { + [c m:getStrongSmall()]; +} + #endif /* USESTRUCT */ diff --git a/clang/test/CodeGenObjC/weak-in-c-struct.m b/clang/test/CodeGenObjC/weak-in-c-struct.m index 4bebc20625eb8e..ac844a8a89355f 100644 --- a/clang/test/CodeGenObjC/weak-in-c-struct.m +++ b/clang/test/CodeGenObjC/weak-in-c-struct.m @@ -42,6 +42,10 @@ // ARM64: %[[V3:.*]] = bitcast i8* %[[V2]] to i8** // ARM64: call void @llvm.objc.destroyWeak(i8** %[[V3]]) +@interface C +- (void)m:(Weak)a; +@end + void test_constructor_destructor_Weak(void) { Weak t; } @@ -191,3 +195,18 @@ void test_argument_Weak(Weak *a) { Weak test_return_Weak(Weak *a) { return *a; } + +// COMMON-LABEL: define void @test_null_receiver( +// COMMON: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]] +// COMMON: br i1 + +// COMMON: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %[[STRUCT_WEAK]]*)*)({{.*}}, %[[STRUCT_WEAK]]* %[[AGG_TMP]]) +// COMMON: br + +// COMMON: %[[V6:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_TMP]] to i8** +// COMMON: call void @__destructor_{{.*}}(i8** %[[V6]]) +// COMMON: br + +void test_null_receiver(C *c) { + [c m:getWeak()]; +} diff --git a/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm b/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm index bd89c8629a7ced..efaced63aa42ac 100644 --- a/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm +++ b/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm @@ -13,6 +13,7 @@ // CHECK: %[[STRUCT_STRONG:.*]] = type { i8* } // CHECK: %[[STRUCT_S:.*]] = type { i8* } // CHECK: %[[STRUCT_CONTAINSNONTRIVIAL:.*]] = type { %{{.*}}, i8* } +// CHECK: %[[STRUCT_NONTRIVIAL:.*]] = type { i32* } #ifdef TRIVIALABI struct __attribute__((trivial_abi)) StrongWeak { @@ -69,6 +70,12 @@ struct __attribute__((trivial_abi)) S { id f1; }; +@interface C +- (void)passStrong:(Strong)a; +- (void)passStrongWeak:(StrongWeak)a; +- (void)passNonTrivial:(NonTrivial)a; +@end + // CHECK: define void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %{{.*}}) // CHECK: call %struct.StrongWeak* @_ZN10StrongWeakD1Ev( // CHECK-NEXT: ret void @@ -207,3 +214,49 @@ void testCallContainsNonTrivial(ContainsNonTrivial *a) { Strong D0::m0() { return {}; } } + +namespace testNullReceiver { + +// CHECK-LABEL: define void @_ZN16testNullReceiver5test0EP1C( +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8 +// CHECK: br i1 + +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0 +// CHECK: %[[V7:.*]] = load i8*, i8** %[[COERCE_DIVE]], align 8 +// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i8* %[[V7]] to i64 +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i64)*)({{.*}}, i64 %[[COERCE_VAL_PI]]) +// CHECK: br + +// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONG]]* @_ZN6StrongD1Ev(%[[STRUCT_STRONG]]* nonnull dereferenceable(8) %[[AGG_TMP]]) +// CHECK: br + +void test0(C *c) { + [c passStrong:Strong()]; +} + +// CHECK-LABEL: define void @_ZN16testNullReceiver5test1EP1C( +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONGWEAK]], align 8 +// CHECK: br i1 + +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void ({{.*}}, %[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]]) +// CHECK: br + +// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakD1Ev(%[[STRUCT_STRONGWEAK]]* nonnull dereferenceable(16) %[[AGG_TMP]]) +// CHECK: br + +void test1(C *c) { + [c passStrongWeak:StrongWeak()]; +} + +// No null check needed. + +// CHECK-LABEL: define void @_ZN16testNullReceiver5test2EP1C( +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_NONTRIVIAL]], align 8 +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %[[STRUCT_NONTRIVIAL]]*)*)({{.*}}, %[[STRUCT_NONTRIVIAL]]* %[[AGG_TMP]]) +// CHECK-NEXT: call %[[STRUCT_NONTRIVIAL]]* @_ZN10NonTrivialD1Ev(%[[STRUCT_NONTRIVIAL]]* nonnull dereferenceable(8) %[[AGG_TMP]]) + +void test2(C *c) { + [c passNonTrivial:NonTrivial()]; +} + +}