mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-18 16:58:23 +00:00
[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:
parent
cf8f682c2d
commit
34405b41d6
@ -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;
|
||||
|
@ -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() &&
|
||||
|
@ -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;
|
||||
|
34
clang/test/CodeGenObjC/objc-dispatch-null-check.m
Normal file
34
clang/test/CodeGenObjC/objc-dispatch-null-check.m
Normal 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()];
|
||||
}
|
@ -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 */
|
||||
|
@ -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()];
|
||||
}
|
||||
|
@ -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()];
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user