[CodeGen][ObjC] Destroy callee-destroyed arguments in the caller

function when the receiver is nil

Callee-destroyed arguments to a method have to be destroyed in the
caller function when the receiver is nil as the method doesn't get
executed. This fixes PR48207.

rdar://71808391

Differential Revision: https://reviews.llvm.org/D93273
This commit is contained in:
Akira Hatanaka 2020-12-28 11:52:27 -08:00
parent cf8f682c2d
commit 34405b41d6
7 changed files with 158 additions and 2 deletions

View File

@ -1664,6 +1664,9 @@ public:
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;

View File

@ -2738,6 +2738,17 @@ SourceRange ParmVarDecl::getSourceRange() const {
return DeclaratorDecl::getSourceRange();
}
bool ParmVarDecl::isDestroyedInCallee() const {
if (hasAttr<NSConsumedAttr>())
return true;
auto *RT = getType()->getAs<RecordType>();
if (RT && RT->getDecl()->isParamDestroyedInCallee())
return true;
return false;
}
Expr *ParmVarDecl::getDefaultArg() {
assert(!hasUnparsedDefaultArg() && "Default argument is not yet parsed!");
assert(!hasUninstantiatedDefaultArg() &&

View File

@ -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<RecordType>();
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<NSConsumedAttr>()) {
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<NSConsumedAttr>()) {
if (ParamDecl->isDestroyedInCallee()) {
if (!nullReturn.NullBB)
nullReturn.init(CGF, arg0);
requiresnullCheck = true;

View File

@ -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()];
}

View File

@ -91,6 +91,7 @@ void func(Strong *);
@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 */

View File

@ -42,6 +42,10 @@ void calleeWeak(Weak);
// 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()];
}

View File

@ -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 ContainsNonTrivial {
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 @@ struct D0 : B0, B1 {
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()];
}
}