Skip to content

Commit 28f7087

Browse files
committed
[CodeGen][ObjC] Call synthesized copy constructor/assignment operator
functions in getter/setter functions of non-trivial C struct properties This fixes a bug where the getter/setter functions were doing a trivial copy instead of calling the synthesized functions that copy non-trivial C struct types. This fixes llvm#56680. Differential Revision: https://reviews.llvm.org/D131701
1 parent ccde601 commit 28f7087

File tree

2 files changed

+138
-15
lines changed

2 files changed

+138
-15
lines changed

clang/lib/CodeGen/CGObjC.cpp

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "clang/AST/StmtObjC.h"
2323
#include "clang/Basic/Diagnostic.h"
2424
#include "clang/CodeGen/CGFunctionInfo.h"
25+
#include "clang/CodeGen/CodeGenABITypes.h"
2526
#include "llvm/ADT/STLExtras.h"
2627
#include "llvm/Analysis/ObjCARCUtil.h"
2728
#include "llvm/BinaryFormat/MachO.h"
@@ -1136,6 +1137,23 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
11361137
const ObjCPropertyImplDecl *propImpl,
11371138
const ObjCMethodDecl *GetterMethodDecl,
11381139
llvm::Constant *AtomicHelperFn) {
1140+
1141+
ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
1142+
1143+
if (ivar->getType().isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct) {
1144+
if (!AtomicHelperFn) {
1145+
LValue Src =
1146+
EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), ivar, 0);
1147+
LValue Dst = MakeAddrLValue(ReturnValue, ivar->getType());
1148+
callCStructCopyConstructor(Dst, Src);
1149+
} else {
1150+
ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
1151+
emitCPPObjectAtomicGetterCall(*this, ReturnValue.getPointer(), ivar,
1152+
AtomicHelperFn);
1153+
}
1154+
return;
1155+
}
1156+
11391157
// If there's a non-trivial 'get' expression, we just have to emit that.
11401158
if (!hasTrivialGetExpr(propImpl)) {
11411159
if (!AtomicHelperFn) {
@@ -1156,8 +1174,6 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
11561174
QualType propType = prop->getType();
11571175
ObjCMethodDecl *getterMethod = propImpl->getGetterMethodDecl();
11581176

1159-
ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
1160-
11611177
// Pick an implementation strategy.
11621178
PropertyImplStrategy strategy(CGM, propImpl);
11631179
switch (strategy.getKind()) {
@@ -1424,6 +1440,24 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
14241440
ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
14251441
ObjCMethodDecl *setterMethod = propImpl->getSetterMethodDecl();
14261442

1443+
if (ivar->getType().isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct) {
1444+
ParmVarDecl *PVD = *setterMethod->param_begin();
1445+
if (!AtomicHelperFn) {
1446+
// Call the move assignment operator instead of calling the copy
1447+
// assignment operator and destructor.
1448+
LValue Dst = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), ivar,
1449+
/*quals*/ 0);
1450+
LValue Src = MakeAddrLValue(GetAddrOfLocalVar(PVD), ivar->getType());
1451+
callCStructMoveAssignmentOperator(Dst, Src);
1452+
} else {
1453+
// If atomic, assignment is called via a locking api.
1454+
emitCPPObjectAtomicSetterCall(*this, setterMethod, ivar, AtomicHelperFn);
1455+
}
1456+
// Decativate the destructor for the setter parameter.
1457+
DeactivateCleanupBlock(CalleeDestructedParamCleanups[PVD], AllocaInsertPt);
1458+
return;
1459+
}
1460+
14271461
// Just use the setter expression if Sema gave us one and it's
14281462
// non-trivial.
14291463
if (!hasTrivialSetExpr(propImpl)) {
@@ -3680,23 +3714,34 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) {
36803714
llvm::Constant *
36813715
CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction(
36823716
const ObjCPropertyImplDecl *PID) {
3717+
const ObjCPropertyDecl *PD = PID->getPropertyDecl();
3718+
if ((!(PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_atomic)))
3719+
return nullptr;
3720+
3721+
QualType Ty = PID->getPropertyIvarDecl()->getType();
3722+
ASTContext &C = getContext();
3723+
3724+
if (Ty.isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct) {
3725+
// Call the move assignment operator instead of calling the copy assignment
3726+
// operator and destructor.
3727+
CharUnits Alignment = C.getTypeAlignInChars(Ty);
3728+
llvm::Constant *Fn = getNonTrivialCStructMoveAssignmentOperator(
3729+
CGM, Alignment, Alignment, Ty.isVolatileQualified(), Ty);
3730+
return llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy);
3731+
}
3732+
36833733
if (!getLangOpts().CPlusPlus ||
36843734
!getLangOpts().ObjCRuntime.hasAtomicCopyHelper())
36853735
return nullptr;
3686-
QualType Ty = PID->getPropertyIvarDecl()->getType();
36873736
if (!Ty->isRecordType())
36883737
return nullptr;
3689-
const ObjCPropertyDecl *PD = PID->getPropertyDecl();
3690-
if ((!(PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_atomic)))
3691-
return nullptr;
36923738
llvm::Constant *HelperFn = nullptr;
36933739
if (hasTrivialSetExpr(PID))
36943740
return nullptr;
36953741
assert(PID->getSetterCXXAssignment() && "SetterCXXAssignment - null");
36963742
if ((HelperFn = CGM.getAtomicSetterHelperFnMap(Ty)))
36973743
return HelperFn;
36983744

3699-
ASTContext &C = getContext();
37003745
IdentifierInfo *II
37013746
= &CGM.getContext().Idents.get("__assign_helper_atomic_property_");
37023747

@@ -3767,26 +3812,34 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction(
37673812
return HelperFn;
37683813
}
37693814

3770-
llvm::Constant *
3771-
CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction(
3772-
const ObjCPropertyImplDecl *PID) {
3815+
llvm::Constant *CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction(
3816+
const ObjCPropertyImplDecl *PID) {
3817+
const ObjCPropertyDecl *PD = PID->getPropertyDecl();
3818+
if ((!(PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_atomic)))
3819+
return nullptr;
3820+
3821+
QualType Ty = PD->getType();
3822+
ASTContext &C = getContext();
3823+
3824+
if (Ty.isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct) {
3825+
CharUnits Alignment = C.getTypeAlignInChars(Ty);
3826+
llvm::Constant *Fn = getNonTrivialCStructCopyConstructor(
3827+
CGM, Alignment, Alignment, Ty.isVolatileQualified(), Ty);
3828+
return llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy);
3829+
}
3830+
37733831
if (!getLangOpts().CPlusPlus ||
37743832
!getLangOpts().ObjCRuntime.hasAtomicCopyHelper())
37753833
return nullptr;
3776-
const ObjCPropertyDecl *PD = PID->getPropertyDecl();
3777-
QualType Ty = PD->getType();
37783834
if (!Ty->isRecordType())
37793835
return nullptr;
3780-
if ((!(PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_atomic)))
3781-
return nullptr;
37823836
llvm::Constant *HelperFn = nullptr;
37833837
if (hasTrivialGetExpr(PID))
37843838
return nullptr;
37853839
assert(PID->getGetterCXXConstructor() && "getGetterCXXConstructor - null");
37863840
if ((HelperFn = CGM.getAtomicGetterHelperFnMap(Ty)))
37873841
return HelperFn;
37883842

3789-
ASTContext &C = getContext();
37903843
IdentifierInfo *II =
37913844
&CGM.getContext().Idents.get("__copy_helper_atomic_property_");
37923845

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -emit-llvm -o - %s | FileCheck %s
2+
3+
typedef struct {
4+
id x;
5+
} S0;
6+
7+
@interface C {
8+
S0 _p1;
9+
}
10+
@property(nonatomic) S0 nonatomic;
11+
@property S0 atomic0;
12+
@property S0 p1;
13+
-(S0)p1;
14+
-(void)setP1:(S0)s0;
15+
@end
16+
17+
@implementation C
18+
-(S0)p1 {
19+
return _p1;
20+
}
21+
-(void)setP1:(S0)s0 {
22+
_p1 = s0;
23+
}
24+
@end
25+
26+
// CHECK: %[[STRUCT_S0:.*]] = type { ptr }
27+
28+
// Check that parameters of user-defined setters are destructed.
29+
30+
// CHECK-LABEL: define internal void @"\01-[C setP1:]"(
31+
// CHECK: %[[S0:.*]] = alloca %[[STRUCT_S0]], align 8
32+
// CHECK: call void @__copy_assignment_8_8_s0(ptr %{{.*}}, ptr %[[S0]])
33+
// CHECK: call void @__destructor_8_s0(ptr %[[S0]])
34+
35+
// CHECK: define internal i64 @"\01-[C nonatomic]"(ptr noundef %[[SELF:.*]], {{.*}})
36+
// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_S0]], align 8
37+
// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8
38+
// CHECK: store ptr %[[SELF]], ptr %[[SELF_ADDR]], align 8
39+
// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8
40+
// CHECK: %[[IVAR:.*]] = load i32, ptr @"OBJC_IVAR_$_C._nonatomic", align 8
41+
// CHECK: %[[IVAR_CONV:.*]] = sext i32 %[[IVAR]] to i64
42+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[IVAR_CONV]]
43+
// CHECK: call void @__copy_constructor_8_8_s0(ptr %[[RETVAL]], ptr %[[ADD_PTR]])
44+
// CHECK-NOT: call
45+
// CHECK: ret i64
46+
47+
// CHECK: define internal void @"\01-[C setNonatomic:]"(ptr noundef %[[SELF:.*]], {{.*}}, i64 %[[NONATOMIC_COERCE:.*]])
48+
// CHECK: %[[NONATOMIC:.*]] = alloca %[[STRUCT_S0]], align 8
49+
// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8
50+
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[NONATOMIC]], i32 0, i32 0
51+
// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[NONATOMIC_COERCE]] to ptr
52+
// CHECK: store ptr %[[COERCE_VAL_IP]], ptr %[[COERCE_DIVE]], align 8
53+
// CHECK: store ptr %[[SELF]], ptr %[[SELF_ADDR]], align 8
54+
// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8
55+
// CHECK: %[[IVAR:.*]] = load i32, ptr @"OBJC_IVAR_$_C._nonatomic", align 8
56+
// CHECK: %[[IVAR_CONV:.*]] = sext i32 %[[IVAR]] to i64
57+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[IVAR_CONV]]
58+
// CHECK: call void @__move_assignment_8_8_s0(ptr %[[ADD_PTR]], ptr %[[NONATOMIC]])
59+
// CHECK-NOT: call
60+
// CHECK: ret void
61+
62+
// CHECK-LABEL: define internal i64 @"\01-[C atomic0]"(
63+
// CHECK: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__copy_constructor_8_8_s0)
64+
// CHECK-NOT: call
65+
// CHECK: ret i64
66+
67+
// CHECK-LABEL: define internal void @"\01-[C setAtomic0:]"(
68+
// CHECK: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__move_assignment_8_8_s0)
69+
// CHECK-NOT: call
70+
// CHECK: ret void

0 commit comments

Comments
 (0)