Skip to content

Commit 34405b4

Browse files
committed
[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
1 parent cf8f682 commit 34405b4

File tree

7 files changed

+158
-2
lines changed

7 files changed

+158
-2
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,9 @@ class ParmVarDecl : public VarDecl {
16641664
return ParmVarDeclBits.IsObjCMethodParam;
16651665
}
16661666

1667+
/// Determines whether this parameter is destroyed in the callee function.
1668+
bool isDestroyedInCallee() const;
1669+
16671670
unsigned getFunctionScopeDepth() const {
16681671
if (ParmVarDeclBits.IsObjCMethodParam) return 0;
16691672
return ParmVarDeclBits.ScopeDepthOrObjCQuals;

clang/lib/AST/Decl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,17 @@ SourceRange ParmVarDecl::getSourceRange() const {
27382738
return DeclaratorDecl::getSourceRange();
27392739
}
27402740

2741+
bool ParmVarDecl::isDestroyedInCallee() const {
2742+
if (hasAttr<NSConsumedAttr>())
2743+
return true;
2744+
2745+
auto *RT = getType()->getAs<RecordType>();
2746+
if (RT && RT->getDecl()->isParamDestroyedInCallee())
2747+
return true;
2748+
2749+
return false;
2750+
}
2751+
27412752
Expr *ParmVarDecl::getDefaultArg() {
27422753
assert(!hasUnparsedDefaultArg() && "Default argument is not yet parsed!");
27432754
assert(!hasUninstantiatedDefaultArg() &&

clang/lib/CodeGen/CGObjCMac.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,24 @@ struct NullReturnState {
17651765
assert(RV.isScalar() &&
17661766
"NullReturnState::complete - arg not on object");
17671767
CGF.EmitARCRelease(RV.getScalarVal(), ARCImpreciseLifetime);
1768+
} else {
1769+
QualType QT = ParamDecl->getType();
1770+
auto *RT = QT->getAs<RecordType>();
1771+
if (RT && RT->getDecl()->isParamDestroyedInCallee()) {
1772+
RValue RV = I->getRValue(CGF);
1773+
QualType::DestructionKind DtorKind = QT.isDestructedType();
1774+
switch (DtorKind) {
1775+
case QualType::DK_cxx_destructor:
1776+
CGF.destroyCXXObject(CGF, RV.getAggregateAddress(), QT);
1777+
break;
1778+
case QualType::DK_nontrivial_c_struct:
1779+
CGF.destroyNonTrivialCStruct(CGF, RV.getAggregateAddress(), QT);
1780+
break;
1781+
default:
1782+
llvm_unreachable("unexpected dtor kind");
1783+
break;
1784+
}
1785+
}
17681786
}
17691787
}
17701788
}
@@ -2241,7 +2259,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
22412259
// Emit a null-check if there's a consumed argument other than the receiver.
22422260
if (!RequiresNullCheck && CGM.getLangOpts().ObjCAutoRefCount && Method) {
22432261
for (const auto *ParamDecl : Method->parameters()) {
2244-
if (ParamDecl->hasAttr<NSConsumedAttr>()) {
2262+
if (ParamDecl->isDestroyedInCallee()) {
22452263
RequiresNullCheck = true;
22462264
break;
22472265
}
@@ -7350,7 +7368,7 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF,
73507368
bool requiresnullCheck = false;
73517369
if (CGM.getLangOpts().ObjCAutoRefCount && method)
73527370
for (const auto *ParamDecl : method->parameters()) {
7353-
if (ParamDecl->hasAttr<NSConsumedAttr>()) {
7371+
if (ParamDecl->isDestroyedInCallee()) {
73547372
if (!nullReturn.NullBB)
73557373
nullReturn.init(CGF, arg0);
73567374
requiresnullCheck = true;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-dispatch-method=non-legacy -fobjc-arc -emit-llvm -o - %s | FileCheck %s
2+
3+
// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* }
4+
5+
typedef struct {
6+
id x;
7+
} Strong;
8+
9+
Strong getStrong(void);
10+
11+
@interface I0
12+
- (void)passStrong:(Strong)a;
13+
@end
14+
15+
// CHECK-LABEL: define void @test0(
16+
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8
17+
// CHECK: %[[CALL:.*]] = call i8* @getStrong()
18+
// CHECK-NEXT: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0
19+
// CHECK-NEXT: store i8* %[[CALL]], i8** %[[COERCE_DIVE]], align 8
20+
21+
// CHECK: %[[MSGSEND_FN:.*]] = load i8*, i8**
22+
// CHECK: %[[V5:.*]] = bitcast i8* %[[MSGSEND_FN]] to void (i8*, i8*, i8*)*
23+
// CHECK: %[[COERCE_DIVE1:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0
24+
// CHECK: %[[V6:.*]] = load i8*, i8** %[[COERCE_DIVE1]], align 8
25+
// CHECK: call void %[[V5]]({{.*}}, i8* %[[V6]])
26+
// CHECK: br
27+
28+
// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONG]]* %[[AGG_TMP]] to i8**
29+
// CHECK: call void @__destructor_8_s0(i8** %[[V7]])
30+
// CHECK: br
31+
32+
void test0(I0 *a) {
33+
[a passStrong:getStrong()];
34+
}

clang/test/CodeGenObjC/strong-in-c-struct.m

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191

9292
@interface C
9393
- (StrongSmall)getStrongSmall;
94+
- (void)m:(StrongSmall)s;
9495
+ (StrongSmall)getStrongSmallClass;
9596
@end
9697

@@ -944,4 +945,21 @@ id test_assignment1(void) {
944945
calleeStrongSmall(g2 = g1);
945946
}
946947

948+
// CHECK-LABEL: define void @test_null_reveiver(
949+
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
950+
// CHECK: br i1
951+
952+
// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP]] to [2 x i64]*
953+
// CHECK: %[[V8:.*]] = load [2 x i64], [2 x i64]* %[[V7]], align 8
954+
// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void ({{.*}}, [2 x i64] %[[V8]])
955+
// CHECK: br
956+
957+
// CHECK: %[[V9:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP]] to i8**
958+
// CHECK: call void @__destructor_8_s8(i8** %[[V9]]) #4
959+
// CHECK: br
960+
961+
void test_null_reveiver(C *c) {
962+
[c m:getStrongSmall()];
963+
}
964+
947965
#endif /* USESTRUCT */

clang/test/CodeGenObjC/weak-in-c-struct.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
// ARM64: %[[V3:.*]] = bitcast i8* %[[V2]] to i8**
4343
// ARM64: call void @llvm.objc.destroyWeak(i8** %[[V3]])
4444

45+
@interface C
46+
- (void)m:(Weak)a;
47+
@end
48+
4549
void test_constructor_destructor_Weak(void) {
4650
Weak t;
4751
}
@@ -191,3 +195,18 @@ void test_argument_Weak(Weak *a) {
191195
Weak test_return_Weak(Weak *a) {
192196
return *a;
193197
}
198+
199+
// COMMON-LABEL: define void @test_null_receiver(
200+
// COMMON: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]]
201+
// COMMON: br i1
202+
203+
// COMMON: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %[[STRUCT_WEAK]]*)*)({{.*}}, %[[STRUCT_WEAK]]* %[[AGG_TMP]])
204+
// COMMON: br
205+
206+
// COMMON: %[[V6:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_TMP]] to i8**
207+
// COMMON: call void @__destructor_{{.*}}(i8** %[[V6]])
208+
// COMMON: br
209+
210+
void test_null_receiver(C *c) {
211+
[c m:getWeak()];
212+
}

clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* }
1414
// CHECK: %[[STRUCT_S:.*]] = type { i8* }
1515
// CHECK: %[[STRUCT_CONTAINSNONTRIVIAL:.*]] = type { %{{.*}}, i8* }
16+
// CHECK: %[[STRUCT_NONTRIVIAL:.*]] = type { i32* }
1617

1718
#ifdef TRIVIALABI
1819
struct __attribute__((trivial_abi)) StrongWeak {
@@ -69,6 +70,12 @@ struct __attribute__((trivial_abi)) S {
6970
id f1;
7071
};
7172

73+
@interface C
74+
- (void)passStrong:(Strong)a;
75+
- (void)passStrongWeak:(StrongWeak)a;
76+
- (void)passNonTrivial:(NonTrivial)a;
77+
@end
78+
7279
// CHECK: define void @_Z19testParamStrongWeak10StrongWeak(%[[STRUCT_STRONGWEAK]]* %{{.*}})
7380
// CHECK: call %struct.StrongWeak* @_ZN10StrongWeakD1Ev(
7481
// CHECK-NEXT: ret void
@@ -207,3 +214,49 @@ void testCallContainsNonTrivial(ContainsNonTrivial *a) {
207214
Strong D0::m0() { return {}; }
208215

209216
}
217+
218+
namespace testNullReceiver {
219+
220+
// CHECK-LABEL: define void @_ZN16testNullReceiver5test0EP1C(
221+
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONG]], align 8
222+
// CHECK: br i1
223+
224+
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[AGG_TMP]], i32 0, i32 0
225+
// CHECK: %[[V7:.*]] = load i8*, i8** %[[COERCE_DIVE]], align 8
226+
// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i8* %[[V7]] to i64
227+
// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i64)*)({{.*}}, i64 %[[COERCE_VAL_PI]])
228+
// CHECK: br
229+
230+
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONG]]* @_ZN6StrongD1Ev(%[[STRUCT_STRONG]]* nonnull dereferenceable(8) %[[AGG_TMP]])
231+
// CHECK: br
232+
233+
void test0(C *c) {
234+
[c passStrong:Strong()];
235+
}
236+
237+
// CHECK-LABEL: define void @_ZN16testNullReceiver5test1EP1C(
238+
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_STRONGWEAK]], align 8
239+
// CHECK: br i1
240+
241+
// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void ({{.*}}, %[[STRUCT_STRONGWEAK]]* %[[AGG_TMP]])
242+
// CHECK: br
243+
244+
// CHECK: %[[CALL1:.*]] = call %[[STRUCT_STRONGWEAK]]* @_ZN10StrongWeakD1Ev(%[[STRUCT_STRONGWEAK]]* nonnull dereferenceable(16) %[[AGG_TMP]])
245+
// CHECK: br
246+
247+
void test1(C *c) {
248+
[c passStrongWeak:StrongWeak()];
249+
}
250+
251+
// No null check needed.
252+
253+
// CHECK-LABEL: define void @_ZN16testNullReceiver5test2EP1C(
254+
// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_NONTRIVIAL]], align 8
255+
// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %[[STRUCT_NONTRIVIAL]]*)*)({{.*}}, %[[STRUCT_NONTRIVIAL]]* %[[AGG_TMP]])
256+
// CHECK-NEXT: call %[[STRUCT_NONTRIVIAL]]* @_ZN10NonTrivialD1Ev(%[[STRUCT_NONTRIVIAL]]* nonnull dereferenceable(8) %[[AGG_TMP]])
257+
258+
void test2(C *c) {
259+
[c passNonTrivial:NonTrivial()];
260+
}
261+
262+
}

0 commit comments

Comments
 (0)