Patch adds support for copying of those

objective-c++ class objects which have GC'able objc object
pointers and need to use ObjC's objc_memmove_collectable
API (radar 8070772). 

llvm-svn: 106061
This commit is contained in:
Fariborz Jahanian 2010-06-15 22:44:06 +00:00
parent 438c35b5d1
commit 021510e96f
11 changed files with 233 additions and 38 deletions

View File

@ -540,6 +540,7 @@ LIBBUILTIN(siglongjmp, "vSJi", "fr", "setjmp.h")
// id objc_msgSend(id, SEL)
// but we need new type letters for that.
LIBBUILTIN(objc_msgSend, "v*.", "f", "objc/message.h")
LIBBUILTIN(objc_memmove_collectable, "v*v*vC*z", "nF", "objc/objc-auto.h")
// Builtin math library functions
LIBBUILTIN(pow, "ddd", "fe", "math.h")

View File

@ -14,6 +14,7 @@
#include "TargetInfo.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "CGObjCRuntime.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
@ -483,6 +484,16 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
llvm::ConstantInt::get(llvm::Type::getInt1Ty(VMContext), 0));
return RValue::get(Address);
}
case Builtin::BIobjc_memmove_collectable: {
Value *Address = EmitScalarExpr(E->getArg(0));
Value *SrcAddr = EmitScalarExpr(E->getArg(1));
Value *SizeVal = EmitScalarExpr(E->getArg(2));
CGM.getObjCRuntime().EmitGCMemmoveCollectable(*this,
Address, SrcAddr, SizeVal);
return RValue::get(Address);
}
case Builtin::BImemmove:
case Builtin::BI__builtin_memmove: {
Value *Address = EmitScalarExpr(E->getArg(0));

View File

@ -177,11 +177,16 @@ bool AggExprEmitter::TypeRequiresGCollection(QualType T) {
/// directly into the return value slot. If GC does interfere, a final
/// move will be performed.
void AggExprEmitter::EmitGCMove(const Expr *E, RValue Src) {
if (!RequiresGCollection) return;
CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF, DestPtr,
if (RequiresGCollection) {
std::pair<uint64_t, unsigned> TypeInfo =
CGF.getContext().getTypeInfo(E->getType());
unsigned long size = TypeInfo.first/8;
const llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType());
llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size);
CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF, DestPtr,
Src.getAggregateAddr(),
E->getType());
SizeVal);
}
}
/// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired.
@ -198,9 +203,14 @@ void AggExprEmitter::EmitFinalDestCopy(const Expr *E, RValue Src, bool Ignore) {
}
if (RequiresGCollection) {
std::pair<uint64_t, unsigned> TypeInfo =
CGF.getContext().getTypeInfo(E->getType());
unsigned long size = TypeInfo.first/8;
const llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType());
llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size);
CGF.CGM.getObjCRuntime().EmitGCMemmoveCollectable(CGF,
DestPtr, Src.getAggregateAddr(),
E->getType());
SizeVal);
return;
}
// If the result of the assignment is used, copy the LHS there also.
@ -837,6 +847,30 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr,
if (SrcPtr->getType() != SBP)
SrcPtr = Builder.CreateBitCast(SrcPtr, SBP, "tmp");
if (const RecordType *RecordTy = Ty->getAs<RecordType>()) {
RecordDecl *Record = RecordTy->getDecl();
if (Record->hasObjectMember()) {
unsigned long size = TypeInfo.first/8;
const llvm::Type *SizeTy = ConvertType(getContext().getSizeType());
llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size);
CGM.getObjCRuntime().EmitGCMemmoveCollectable(*this, DestPtr, SrcPtr,
SizeVal);
return;
}
} else if (getContext().getAsArrayType(Ty)) {
QualType BaseType = getContext().getBaseElementType(Ty);
if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) {
if (RecordTy->getDecl()->hasObjectMember()) {
unsigned long size = TypeInfo.first/8;
const llvm::Type *SizeTy = ConvertType(getContext().getSizeType());
llvm::Value *SizeVal = llvm::ConstantInt::get(SizeTy, size);
CGM.getObjCRuntime().EmitGCMemmoveCollectable(*this, DestPtr, SrcPtr,
SizeVal);
return;
}
}
}
Builder.CreateCall5(CGM.getMemCpyFn(DestPtr->getType(), SrcPtr->getType(),
IntPtr),
DestPtr, SrcPtr,

View File

@ -275,10 +275,7 @@ CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
llvm::Value *Src = EmitLValue(E->getArg(1)).getAddress();
QualType Ty = E->getType();
if (ClassDecl->hasObjectMember())
CGM.getObjCRuntime().EmitGCMemmoveCollectable(*this, This, Src, Ty);
else
EmitAggregateCopy(This, Src, Ty);
EmitAggregateCopy(This, Src, Ty);
return RValue::get(This);
}
}

View File

@ -197,7 +197,7 @@ public:
virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
llvm::Value *DestPtr,
llvm::Value *SrcPtr,
QualType Ty);
llvm::Value *Size);
virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF,
QualType ObjectTy,
llvm::Value *BaseValue,
@ -2174,17 +2174,12 @@ void CGObjCGNU::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF,
void CGObjCGNU::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
llvm::Value *DestPtr,
llvm::Value *SrcPtr,
QualType Ty) {
llvm::Value *Size) {
CGBuilderTy B = CGF.Builder;
DestPtr = EnforceType(B, DestPtr, IdTy);
SrcPtr = EnforceType(B, SrcPtr, PtrToIdTy);
std::pair<uint64_t, unsigned> TypeInfo = CGM.getContext().getTypeInfo(Ty);
unsigned long size = TypeInfo.first/8;
// FIXME: size_t
llvm::Value *N = llvm::ConstantInt::get(LongTy, size);
B.CreateCall3(MemMoveFn, DestPtr, SrcPtr, N);
B.CreateCall3(MemMoveFn, DestPtr, SrcPtr, Size);
}
llvm::GlobalVariable *CGObjCGNU::ObjCIvarOffsetVariable(

View File

@ -1196,7 +1196,7 @@ public:
llvm::Value *src, llvm::Value *dest);
virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
llvm::Value *dest, llvm::Value *src,
QualType Ty);
llvm::Value *size);
virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF,
QualType ObjectTy,
@ -1438,7 +1438,7 @@ public:
llvm::Value *src, llvm::Value *dest);
virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
llvm::Value *dest, llvm::Value *src,
QualType Ty);
llvm::Value *size);
virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF,
QualType ObjectTy,
llvm::Value *BaseValue,
@ -2938,15 +2938,11 @@ void CGObjCMac::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF,
void CGObjCMac::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
llvm::Value *DestPtr,
llvm::Value *SrcPtr,
QualType Ty) {
// Get size info for this aggregate.
std::pair<uint64_t, unsigned> TypeInfo = CGM.getContext().getTypeInfo(Ty);
unsigned long size = TypeInfo.first/8;
llvm::Value *size) {
SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, ObjCTypes.Int8PtrTy);
DestPtr = CGF.Builder.CreateBitCast(DestPtr, ObjCTypes.Int8PtrTy);
llvm::Value *N = llvm::ConstantInt::get(ObjCTypes.LongTy, size);
CGF.Builder.CreateCall3(ObjCTypes.GcMemmoveCollectableFn(),
DestPtr, SrcPtr, N);
DestPtr, SrcPtr, size);
return;
}
@ -5476,15 +5472,11 @@ void CGObjCNonFragileABIMac::EmitGCMemmoveCollectable(
CodeGen::CodeGenFunction &CGF,
llvm::Value *DestPtr,
llvm::Value *SrcPtr,
QualType Ty) {
// Get size info for this aggregate.
std::pair<uint64_t, unsigned> TypeInfo = CGM.getContext().getTypeInfo(Ty);
unsigned long size = TypeInfo.first/8;
llvm::Value *Size) {
SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, ObjCTypes.Int8PtrTy);
DestPtr = CGF.Builder.CreateBitCast(DestPtr, ObjCTypes.Int8PtrTy);
llvm::Value *N = llvm::ConstantInt::get(ObjCTypes.LongTy, size);
CGF.Builder.CreateCall3(ObjCTypes.GcMemmoveCollectableFn(),
DestPtr, SrcPtr, N);
DestPtr, SrcPtr, Size);
return;
}

View File

@ -208,7 +208,7 @@ public:
virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
llvm::Value *DestPtr,
llvm::Value *SrcPtr,
QualType Ty) = 0;
llvm::Value *Size) = 0;
};
/// Creates an instance of an Objective-C runtime class.

View File

@ -6232,6 +6232,12 @@ void Sema::ActOnFields(Scope* S,
(FD->getType()->isObjCObjectPointerType() ||
FD->getType().isObjCGCStrong()))
Record->setHasObjectMember(true);
else if (Context.getAsArrayType(FD->getType())) {
QualType BaseType = Context.getBaseElementType(FD->getType());
if (Record && BaseType->isRecordType() &&
BaseType->getAs<RecordType>()->getDecl()->hasObjectMember())
Record->setHasObjectMember(true);
}
// Keep track of the number of named members.
if (FD->getIdentifier())
++NumNamedMembers;

View File

@ -4557,6 +4557,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
// \brief Reference to the __builtin_memcpy function.
Expr *BuiltinMemCpyRef = 0;
// \brief Reference to the objc_memmove_collectable function.
Expr *CollectableMemCpyRef = 0;
// Assign non-static members.
for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
@ -4633,9 +4635,34 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
// Take the address of the field references for "from" and "to".
From = CreateBuiltinUnaryOp(Loc, UnaryOperator::AddrOf, move(From));
To = CreateBuiltinUnaryOp(Loc, UnaryOperator::AddrOf, move(To));
bool NeedsCollectableMemCpy =
(BaseType->isRecordType() &&
BaseType->getAs<RecordType>()->getDecl()->hasObjectMember());
if (NeedsCollectableMemCpy) {
if (!CollectableMemCpyRef) {
// Create a reference to the objc_memmove_collectable function.
LookupResult R(*this, &Context.Idents.get("objc_memmove_collectable"),
Loc, LookupOrdinaryName);
LookupName(R, TUScope, true);
FunctionDecl *CollectableMemCpy = R.getAsSingle<FunctionDecl>();
if (!CollectableMemCpy) {
// Something went horribly wrong earlier, and we will have
// complained about it.
Invalid = true;
continue;
}
CollectableMemCpyRef = BuildDeclRefExpr(CollectableMemCpy,
CollectableMemCpy->getType(),
Loc, 0).takeAs<Expr>();
assert(CollectableMemCpyRef && "Builtin reference cannot fail");
}
}
// Create a reference to the __builtin_memcpy builtin function.
if (!BuiltinMemCpyRef) {
else if (!BuiltinMemCpyRef) {
LookupResult R(*this, &Context.Idents.get("__builtin_memcpy"), Loc,
LookupOrdinaryName);
LookupName(R, TUScope, true);
@ -4661,10 +4688,12 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
llvm::SmallVector<SourceLocation, 4> Commas; // FIXME: Silly
Commas.push_back(Loc);
Commas.push_back(Loc);
OwningExprResult Call = ActOnCallExpr(/*Scope=*/0,
Owned(BuiltinMemCpyRef->Retain()),
Loc, move_arg(CallArgs),
Commas.data(), Loc);
OwningExprResult Call = ActOnCallExpr(/*Scope=*/0,
NeedsCollectableMemCpy ?
Owned(CollectableMemCpyRef->Retain()) :
Owned(BuiltinMemCpyRef->Retain()),
Loc, move_arg(CallArgs),
Commas.data(), Loc);
assert(!Call.isInvalid() && "Call to __builtin_memcpy cannot fail!");
Statements.push_back(Call.takeAs<Expr>());
continue;

View File

@ -0,0 +1,57 @@
// RUN: %clang_cc1 -fobjc-gc -emit-llvm -triple x86_64-apple-darwin10.0.0 -o - %s | FileCheck %s
struct A {
A &operator=(const A&);
A &operator=(A&);
};
struct B {
B &operator=(B&);
};
struct C {
virtual C& operator=(const C&);
};
struct POD {
id myobjc;
int array[3][4];
};
struct CopyByValue {
CopyByValue(const CopyByValue&);
CopyByValue &operator=(CopyByValue);
};
struct D : A, B, virtual C {
int scalar;
int scalar_array[2][3];
B class_member;
C class_member_array[2][3];
POD pod_array[2][3];
union {
int x;
float f[3];
};
CopyByValue by_value;
};
void test_D(D d1, D d2) {
d1 = d2;
}
// CHECK: define linkonce_odr %struct.D* @_ZN1DaSERS_
// CHECK: {{call.*_ZN1AaSERS_}}
// CHECK: {{call.*_ZN1BaSERS_}}
// CHECK: {{call.*_ZN1CaSERKS_}}
// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 24}}
// CHECK: {{call.*_ZN1BaSERS_}}
// CHECK: br
// CHECK: {{call.*_ZN1CaSERKS_}}
// CHECK: {{call.*@objc_memmove_collectable}}
// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 12}}
// CHECK: call void @_ZN11CopyByValueC1ERKS_
// CHECK: {{call.*_ZN11CopyByValueaSES_}}
// CHECK: ret

View File

@ -0,0 +1,73 @@
// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
struct A {
A();
A(const A&);
A(A&);
~A();
};
struct B {
B();
B(B&);
};
struct C {
C() {}
C(C& other, A a = A());
int i, j;
};
struct POD {
id myobjc;
int array[3][4];
};
struct D : A, B, virtual C {
D();
int scalar;
int scalar_array[2][3];
B class_member;
C class_member_array[2][3];
POD pod_array[2][3];
union {
int x;
float f[3];
};
};
void f(D d) {
D d2(d);
}
// CHECK: define linkonce_odr void @_ZN1DC1ERS_
// CHECK: call void @_ZN1AC1Ev
// CHECK: call void @_ZN1CC2ERS_1A
// CHECK: call void @_ZN1AD1Ev
// CHECK: call void @_ZN1AC2ERS_
// CHECK: call void @_ZN1BC2ERS_
// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 24}}
// CHECK: call void @_ZN1BC1ERS_
// CHECK: br
// CHECK: {{icmp ult.*, 2}}
// CHECK: {{icmp ult.*, 3}}
// CHECK: call void @_ZN1AC1Ev
// CHECK: call void @_ZN1CC1ERS_1A
// CHECK: call void @_ZN1AD1Ev
// CHECK: {{call.*@objc_memmove_collectable}}
// CHECK: {{call void @llvm.memcpy.p0i8.p0i8.i64.*i64 12}}
// CHECK: ret void
template<class T> struct X0 { void f0(T * ) { } };
template <class > struct X1 { X1( X1& , int = 0 ) { } };
struct X2 { X1<int> result; };
void test_X2()
{
typedef X2 impl;
typedef X0<impl> pimpl;
impl* i;
pimpl pdata;
pdata.f0( new impl(*i));
}