From 20abee6b95c4f5a61e471b4b616439280ca4a81b Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Tue, 10 Jan 2012 00:37:01 +0000 Subject: [PATCH] objc++: patch for IRgen for atomic properties of c++ objects with non-trivial assignment/copy functions. Also, one additional sema check. // rdar://6137845 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147817 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 6 +- lib/CodeGen/CGObjC.cpp | 194 +++++++++++++++--- lib/CodeGen/CodeGenFunction.h | 7 +- lib/Sema/SemaObjCProperty.cpp | 8 +- .../property-object-reference-2.mm | 56 +++++ test/SemaObjCXX/property-reference.mm | 4 +- test/SemaObjCXX/property-synthesis-error.mm | 4 +- 7 files changed, 233 insertions(+), 46 deletions(-) create mode 100644 test/CodeGenObjCXX/property-object-reference-2.mm diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e1c2c7599a..20a4062a74 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -524,9 +524,9 @@ def warn_atomic_property_rule : Warning< InGroup>; def note_atomic_property_fixup_suggest : Note<"setter and getter must both be " "synthesized, or both be user defined,or the property must be nonatomic">; -def warn_atomic_property_nontrivial_assign_op : Warning< - "atomic property of type %0 synthesizing setter using non-trivial assignment" - " operator">, InGroup>; +def err_atomic_property_nontrivial_assign_op : Error< + "atomic property of reference type %0 cannot have non-trivial assignment" + " operator">; def warn_owning_getter_rule : Warning< "property's synthesized getter follows Cocoa naming" " convention for returning 'owned' objects">, diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index b772361d37..6bf32a50f4 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -565,7 +565,7 @@ PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM, void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, const ObjCPropertyImplDecl *PID) { llvm::Constant *AtomicHelperFn = - GenerateObjCAtomicCopyHelperFunction(PID, false); + GenerateObjCAtomicGetterCopyHelperFunction(PID); const ObjCPropertyDecl *PD = PID->getPropertyDecl(); ObjCMethodDecl *OMD = PD->getGetterMethodDecl(); assert(OMD && "Invalid call to generate getter (empty method)"); @@ -599,15 +599,52 @@ static bool hasTrivialGetExpr(const ObjCPropertyImplDecl *propImpl) { return false; } +/// emitCPPObjectAtomicGetterCall - Call the runtime function to +/// copy the ivar into the resturn slot. +static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, + llvm::Value *returnAddr, + ObjCIvarDecl *ivar, + llvm::Constant *AtomicHelperFn) { + // objc_copyCppObjectAtomic (&returnSlot, &CppObjectIvar, + // AtomicHelperFn); + CallArgList args; + + // The 1st argument is the return Slot. + args.add(RValue::get(returnAddr), CGF.getContext().VoidPtrTy); + + // The 2nd argument is the address of the ivar. + llvm::Value *ivarAddr = + CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), + CGF.LoadObjCSelf(), ivar, 0).getAddress(); + ivarAddr = CGF.Builder.CreateBitCast(ivarAddr, CGF.Int8PtrTy); + args.add(RValue::get(ivarAddr), CGF.getContext().VoidPtrTy); + + // Third argument is the helper function. + args.add(RValue::get(AtomicHelperFn), CGF.getContext().VoidPtrTy); + + llvm::Value *copyCppAtomicObjectFn = + CGF.CGM.getObjCRuntime().GetCppAtomicObjectFunction(); + CGF.EmitCall(CGF.getTypes().getFunctionInfo(CGF.getContext().VoidTy, args, + FunctionType::ExtInfo()), + copyCppAtomicObjectFn, ReturnValueSlot(), args); +} + void CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, llvm::Constant *AtomicHelperFn) { // If there's a non-trivial 'get' expression, we just have to emit that. if (!hasTrivialGetExpr(propImpl)) { - ReturnStmt ret(SourceLocation(), propImpl->getGetterCXXConstructor(), - /*nrvo*/ 0); - EmitReturnStmt(ret); + if (!AtomicHelperFn) { + ReturnStmt ret(SourceLocation(), propImpl->getGetterCXXConstructor(), + /*nrvo*/ 0); + EmitReturnStmt(ret); + } + else { + ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl(); + emitCPPObjectAtomicGetterCall(*this, ReturnValue, + ivar, AtomicHelperFn); + } return; } @@ -817,6 +854,7 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, } + static bool hasTrivialSetExpr(const ObjCPropertyImplDecl *PID) { Expr *setter = PID->getSetterCXXAssignment(); if (!setter) return true; @@ -993,7 +1031,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP, const ObjCPropertyImplDecl *PID) { llvm::Constant *AtomicHelperFn = - GenerateObjCAtomicCopyHelperFunction(PID, true); + GenerateObjCAtomicSetterCopyHelperFunction(PID); const ObjCPropertyDecl *PD = PID->getPropertyDecl(); ObjCMethodDecl *OMD = PD->getSetterMethodDecl(); assert(OMD && "Invalid call to generate setter (empty method)"); @@ -2531,14 +2569,13 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) { Builder.CreateCall(extender, object)->setDoesNotThrow(); } -/// GenerateObjCAtomicCopyHelperFunction - Given a c++ object type with +/// GenerateObjCAtomicSetterCopyHelperFunction - Given a c++ object type with /// non-trivial copy assignment function, produce following helper function. /// static void copyHelper(Ty *dest, const Ty *source) { *dest = *source; } /// llvm::Constant * -CodeGenFunction::GenerateObjCAtomicCopyHelperFunction( - const ObjCPropertyImplDecl *PID, - bool forSetter) { +CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( + const ObjCPropertyImplDecl *PID) { // FIXME. This api is for NeXt runtime only for now. if (!getLangOptions().CPlusPlus || !getLangOptions().NeXTRuntime) return 0; @@ -2546,29 +2583,18 @@ CodeGenFunction::GenerateObjCAtomicCopyHelperFunction( if (!Ty->isRecordType()) return 0; const ObjCPropertyDecl *PD = PID->getPropertyDecl(); - if ((!(PD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_atomic)) - || /* temporary */ true) + if ((!(PD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_atomic))) return 0; llvm::Constant * HelperFn = 0; - if (forSetter) { - if (hasTrivialSetExpr(PID)) - return 0; - assert(PID->getSetterCXXAssignment() && "SetterCXXAssignment - null"); - if ((HelperFn = CGM.getAtomicSetterHelperFnMap(Ty))) - return HelperFn; - } - else { - if (hasTrivialGetExpr(PID)) - return 0; - assert(PID->getGetterCXXConstructor() && "getGetterCXXConstructor - null"); - if ((HelperFn = CGM.getAtomicGetterHelperFnMap(Ty))) - return HelperFn; - } - + if (hasTrivialSetExpr(PID)) + return 0; + assert(PID->getSetterCXXAssignment() && "SetterCXXAssignment - null"); + if ((HelperFn = CGM.getAtomicSetterHelperFnMap(Ty))) + return HelperFn; ASTContext &C = getContext(); IdentifierInfo *II - = &CGM.getContext().Idents.get("__copy_helper_atomic_property_"); + = &CGM.getContext().Idents.get("__assign_helper_atomic_property_"); FunctionDecl *FD = FunctionDecl::Create(C, C.getTranslationUnitDecl(), SourceLocation(), @@ -2596,7 +2622,7 @@ CodeGenFunction::GenerateObjCAtomicCopyHelperFunction( llvm::Function *Fn = llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, - "__copy_helper_atomic_property_", &CGM.getModule()); + "__assign_helper_atomic_property_", &CGM.getModule()); if (CGM.getModuleDebugInfo()) DebugInfo = CGM.getModuleDebugInfo(); @@ -2619,8 +2645,7 @@ CodeGenFunction::GenerateObjCAtomicCopyHelperFunction( VK_LValue, OK_Ordinary, SourceLocation()); Expr *Args[2] = { DST, SRC }; - CallExpr *CalleeExp = forSetter ? cast(PID->getSetterCXXAssignment()) - : cast(PID->getGetterCXXConstructor()); + CallExpr *CalleeExp = cast(PID->getSetterCXXAssignment()); CXXOperatorCallExpr *TheCall = new (C) CXXOperatorCallExpr(C, OO_Equal, CalleeExp->getCallee(), Args, 2, DestTy->getPointeeType(), @@ -2630,12 +2655,113 @@ CodeGenFunction::GenerateObjCAtomicCopyHelperFunction( FinishFunction(); HelperFn = llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); - if (forSetter) - CGM.setAtomicSetterHelperFnMap(Ty, HelperFn); - else - CGM.setAtomicGetterHelperFnMap(Ty, HelperFn); + CGM.setAtomicSetterHelperFnMap(Ty, HelperFn); return HelperFn; +} + +llvm::Constant * +CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( + const ObjCPropertyImplDecl *PID) { + // FIXME. This api is for NeXt runtime only for now. + if (!getLangOptions().CPlusPlus || !getLangOptions().NeXTRuntime) + return 0; + const ObjCPropertyDecl *PD = PID->getPropertyDecl(); + QualType Ty = PD->getType(); + if (!Ty->isRecordType()) + return 0; + if ((!(PD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_atomic))) + return 0; + llvm::Constant * HelperFn = 0; + + if (hasTrivialGetExpr(PID)) + return 0; + assert(PID->getGetterCXXConstructor() && "getGetterCXXConstructor - null"); + if ((HelperFn = CGM.getAtomicGetterHelperFnMap(Ty))) + return HelperFn; + + + ASTContext &C = getContext(); + IdentifierInfo *II + = &CGM.getContext().Idents.get("__copy_helper_atomic_property_"); + FunctionDecl *FD = FunctionDecl::Create(C, + C.getTranslationUnitDecl(), + SourceLocation(), + SourceLocation(), II, C.VoidTy, 0, + SC_Static, + SC_None, + false, + true); + + QualType DestTy = C.getPointerType(Ty); + QualType SrcTy = Ty; + SrcTy.addConst(); + SrcTy = C.getPointerType(SrcTy); + + FunctionArgList args; + ImplicitParamDecl dstDecl(FD, SourceLocation(), 0, DestTy); + args.push_back(&dstDecl); + ImplicitParamDecl srcDecl(FD, SourceLocation(), 0, SrcTy); + args.push_back(&srcDecl); + + const CGFunctionInfo &FI = + CGM.getTypes().getFunctionInfo(C.VoidTy, args, FunctionType::ExtInfo()); + + llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI, false); + + llvm::Function *Fn = + llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, + "__copy_helper_atomic_property_", &CGM.getModule()); + if (CGM.getModuleDebugInfo()) + DebugInfo = CGM.getModuleDebugInfo(); + + + StartFunction(FD, C.VoidTy, Fn, FI, args, SourceLocation()); + + DeclRefExpr *SrcExpr = + new (C) DeclRefExpr(&srcDecl, SrcTy, + VK_RValue, SourceLocation()); + + Expr* SRC = new (C) UnaryOperator(SrcExpr, UO_Deref, SrcTy->getPointeeType(), + VK_LValue, OK_Ordinary, SourceLocation()); + + CXXConstructExpr *CXXConstExpr = + cast(PID->getGetterCXXConstructor()); + + SmallVector ConstructorArgs; + ConstructorArgs.push_back(SRC); + CXXConstructExpr::arg_iterator A = CXXConstExpr->arg_begin(); + ++A; + + for (CXXConstructExpr::arg_iterator AEnd = CXXConstExpr->arg_end(); + A != AEnd; ++A) + ConstructorArgs.push_back(*A); + + CXXConstructExpr *TheCXXConstructExpr = + CXXConstructExpr::Create(C, Ty, SourceLocation(), + CXXConstExpr->getConstructor(), + CXXConstExpr->isElidable(), + &ConstructorArgs[0], ConstructorArgs.size(), + CXXConstExpr->hadMultipleCandidates(), + CXXConstExpr->requiresZeroInitialization(), + CXXConstExpr->getConstructionKind(), SourceRange()); + + DeclRefExpr *DstExpr = + new (C) DeclRefExpr(&dstDecl, DestTy, + VK_RValue, SourceLocation()); + + RValue DV = EmitAnyExpr(DstExpr); + CharUnits Alignment = getContext().getTypeAlignInChars(TheCXXConstructExpr->getType()); + EmitAggExpr(TheCXXConstructExpr, + AggValueSlot::forAddr(DV.getScalarVal(), Alignment, Qualifiers(), + AggValueSlot::IsDestructed, + AggValueSlot::DoesNotNeedGCBarriers, + AggValueSlot::IsNotAliased)); + + FinishFunction(); + HelperFn = llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); + CGM.setAtomicGetterHelperFnMap(Ty, HelperFn); + return HelperFn; } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 938acd3c56..2576fed88d 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1335,9 +1335,10 @@ public: llvm::Constant *GenerateCopyHelperFunction(const CGBlockInfo &blockInfo); llvm::Constant *GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo); - llvm::Constant *GenerateObjCAtomicCopyHelperFunction( - const ObjCPropertyImplDecl *PID, - bool forSetter); + llvm::Constant *GenerateObjCAtomicSetterCopyHelperFunction( + const ObjCPropertyImplDecl *PID); + llvm::Constant *GenerateObjCAtomicGetterCopyHelperFunction( + const ObjCPropertyImplDecl *PID); void BuildBlockRelease(llvm::Value *DeclPtr, BlockFieldFlags flags); diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 6379c924e2..bbc6d23385 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -830,9 +830,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, dyn_cast_or_null(callExpr)) if (const FunctionDecl *FuncDecl = CXXCE->getDirectCallee()) if (!FuncDecl->isTrivial()) - Diag(PropertyLoc, - diag::warn_atomic_property_nontrivial_assign_op) + if (property->getType()->isReferenceType()) { + Diag(PropertyLoc, + diag::err_atomic_property_nontrivial_assign_op) << property->getType(); + Diag(FuncDecl->getLocStart(), + diag::note_callee_decl) << FuncDecl; + } } PIDecl->setSetterCXXAssignment(Res.takeAs()); } diff --git a/test/CodeGenObjCXX/property-object-reference-2.mm b/test/CodeGenObjCXX/property-object-reference-2.mm new file mode 100644 index 0000000000..b150a3e3ea --- /dev/null +++ b/test/CodeGenObjCXX/property-object-reference-2.mm @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s +// rdar://6137845 + +extern int DEFAULT(); + +struct TCPPObject +{ + TCPPObject(); + ~TCPPObject(); + TCPPObject(const TCPPObject& inObj, int i = DEFAULT()); + TCPPObject& operator=(const TCPPObject& inObj); + int filler[64]; +}; + + +@interface MyDocument +{ +@private + TCPPObject _cppObject; + TCPPObject _cppObject1; +} +@property (assign, readwrite, atomic) const TCPPObject MyProperty; +@property (assign, readwrite, atomic) const TCPPObject MyProperty1; +@end + +@implementation MyDocument + @synthesize MyProperty = _cppObject; + @synthesize MyProperty1 = _cppObject1; +@end + +// CHECK: define internal void @__copy_helper_atomic_property_( +// CHECK: [[TWO:%.*]] = load %struct.TCPPObject** [[ADDR:%.*]], align 8 +// CHECK: [[THREE:%.*]] = load %struct.TCPPObject** [[ADDR1:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call i32 @_Z7DEFAULTv() +// CHECK: call void @_ZN10TCPPObjectC1ERKS_i(%struct.TCPPObject* [[TWO]], %struct.TCPPObject* [[THREE]], i32 [[CALL]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument MyProperty]"( +// CHECK: [[ONE:%.*]] = bitcast i8* [[ADDPTR:%.*]] to %struct.TCPPObject* +// CHECK: [[TWO:%.*]] = bitcast %struct.TCPPObject* [[ONE]] to i8* +// CHECK: [[THREE:%.*]] = bitcast %struct.TCPPObject* [[AGGRESULT:%.*]] to i8* +// CHECK: call void @objc_copyCppObjectAtomic(i8* [[THREE]], i8* [[TWO]], i8* bitcast (void (%struct.TCPPObject*, %struct.TCPPObject*)* @__copy_helper_atomic_property_ to i8*)) +// CHECK: ret void + +// CHECK: define internal void @__assign_helper_atomic_property_( +// CHECK: [[TWO:%.*]] = load %struct.TCPPObject** [[ADDR:%.*]], align 8 +// CHECK: [[THREE:%.*]] = load %struct.TCPPObject** [[ADDR1:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call %struct.TCPPObject* @_ZN10TCPPObjectaSERKS_(%struct.TCPPObject* [[TWO]], %struct.TCPPObject* [[THREE]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument setMyProperty:]"( +// CHECK: [[ONE:%.*]] = bitcast i8* [[ADDRPTR:%.*]] to %struct.TCPPObject* +// CHECK: [[TWO:%.*]] = bitcast %struct.TCPPObject* [[ONE]] to i8* +// CHECK: [[THREE:%.*]] = bitcast %struct.TCPPObject* [[MYPROPERTY:%.*]] to i8* +// CHECK: call void @objc_copyCppObjectAtomic(i8* [[TWO]], i8* [[THREE]], i8* bitcast (void (%struct.TCPPObject*, %struct.TCPPObject*)* @__assign_helper_atomic_property_ to i8*)) +// CHECK: ret void diff --git a/test/SemaObjCXX/property-reference.mm b/test/SemaObjCXX/property-reference.mm index 236dba61fc..9cc51d5376 100644 --- a/test/SemaObjCXX/property-reference.mm +++ b/test/SemaObjCXX/property-reference.mm @@ -8,7 +8,7 @@ public: TCPPObject(); ~TCPPObject(); - TCPPObject& operator=(const TCPPObject& inObj)const ; + TCPPObject& operator=(const TCPPObject& inObj)const ; // expected-note {{'operator=' declared here}} void* Data(); @@ -29,7 +29,7 @@ typedef const TCPPObject& CREF_TCPPObject; @implementation TNSObject @synthesize cppObjectNonAtomic; -@synthesize cppObjectAtomic; // expected-warning{{atomic property of type 'CREF_TCPPObject' (aka 'const TCPPObject &') synthesizing setter using non-trivial assignment operator}} +@synthesize cppObjectAtomic; // expected-error{{atomic property of reference type 'CREF_TCPPObject' (aka 'const TCPPObject &') cannot have non-trivial assignment operator}} @dynamic cppObjectDynamic; - (const TCPPObject&) cppObjectNonAtomic diff --git a/test/SemaObjCXX/property-synthesis-error.mm b/test/SemaObjCXX/property-synthesis-error.mm index 5ba4b70a2b..c50756622c 100644 --- a/test/SemaObjCXX/property-synthesis-error.mm +++ b/test/SemaObjCXX/property-synthesis-error.mm @@ -38,7 +38,7 @@ public: TCPPObject(const TCPPObject& inObj); TCPPObject(); ~TCPPObject(); - TCPPObject& operator=(const TCPPObject& inObj); + TCPPObject& operator=(const TCPPObject& inObj); // expected-note {{'operator=' declared here}} private: void* fData; }; @@ -67,7 +67,7 @@ private: @implementation MyDocument -@synthesize cppObject = _cppObject; // expected-warning {{atomic property of type 'const TCPPObject &' synthesizing setter using non-trivial assignment operator}} +@synthesize cppObject = _cppObject; // expected-error {{atomic property of reference type 'const TCPPObject &' cannot have non-trivial assignment operator}} @synthesize ncppObject = _ncppObject; @synthesize tcppObject = _tcppObject; -- 2.40.0