Simplify the generation of Objective-C setters, at least a little.

Use a more portable heuristic for deciding when to emit a single
atomic store;  it's possible that I've lost information here, but
I'm not sure how much of the logic before was intentionally arch-specific
and how much was just not quite consistent.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@139468 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
John McCall 2011-09-10 09:17:20 +00:00
Родитель 33e56f3273
Коммит 71c758d3f4
3 изменённых файлов: 211 добавлений и 136 удалений

Просмотреть файл

@ -581,16 +581,209 @@ void CodeGenFunction::GenerateObjCAtomicSetterBody(ObjCMethodDecl *OMD,
GetCopyStructFn, ReturnValueSlot(), Args);
}
static bool
IvarAssignHasTrvialAssignment(const ObjCPropertyImplDecl *PID,
QualType IvarT) {
bool HasTrvialAssignment = true;
if (PID->getSetterCXXAssignment()) {
const CXXRecordDecl *classDecl = IvarT->getAsCXXRecordDecl();
HasTrvialAssignment =
(!classDecl || classDecl->hasTrivialCopyAssignment());
static bool hasTrivialAssignment(const ObjCPropertyImplDecl *PID) {
Expr *assign = PID->getSetterCXXAssignment();
if (!assign) return true;
// An operator call is trivial if the function it calls is trivial.
if (CallExpr *call = dyn_cast<CallExpr>(assign)) {
if (const FunctionDecl *callee
= dyn_cast_or_null<FunctionDecl>(call->getCalleeDecl()))
if (callee->isTrivial())
return true;
return false;
}
return HasTrvialAssignment;
assert(isa<BinaryOperator>(assign));
return true;
}
/// Should the setter use objc_setProperty?
static bool shouldUseSetProperty(CodeGenFunction &CGF,
ObjCPropertyDecl::SetterKind setterKind) {
// Copy setters require objc_setProperty.
if (setterKind == ObjCPropertyDecl::Copy)
return true;
// So do retain setters, if we're not in GC-only mode (where
// 'retain' is ignored).
if (setterKind == ObjCPropertyDecl::Retain &&
CGF.getLangOptions().getGCMode() != LangOptions::GCOnly)
return true;
// Otherwise, we don't need this.
return false;
}
static bool isAssignmentImplicitlyAtomic(CodeGenFunction &CGF,
const ObjCIvarDecl *ivar) {
// Aggregate assignment is not implicitly atomic if it includes a GC
// barrier.
QualType ivarType = ivar->getType();
if (CGF.getLangOptions().getGCMode())
if (const RecordType *ivarRecordTy = ivarType->getAs<RecordType>())
if (ivarRecordTy->getDecl()->hasObjectMember())
return false;
// Assume that any store no larger than a pointer, and as aligned as
// the required size, can be performed atomically.
ASTContext &Context = CGF.getContext();
std::pair<CharUnits,CharUnits> ivarSizeAndAlignment
= Context.getTypeInfoInChars(ivar->getType());
return (ivarSizeAndAlignment.first
<= CharUnits::fromQuantity(CGF.PointerSizeInBytes) &&
ivarSizeAndAlignment.second >= ivarSizeAndAlignment.first);
}
void
CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
const ObjCPropertyImplDecl *propImpl) {
// Just use the setter expression if Sema gave us one and it's
// non-trivial. There's no way to do this atomically.
if (!hasTrivialAssignment(propImpl)) {
EmitStmt(propImpl->getSetterCXXAssignment());
return;
}
const ObjCPropertyDecl *prop = propImpl->getPropertyDecl();
ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
ObjCMethodDecl *setterMethod = prop->getSetterMethodDecl();
// A property is copy if it says it's copy.
ObjCPropertyDecl::SetterKind setterKind = prop->getSetterKind();
bool isCopy = (setterKind == ObjCPropertyDecl::Copy);
// A property is atomic if it doesn't say it's nonatomic.
bool isAtomic =
!(prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic);
// Should we call the runtime's set property function?
if (shouldUseSetProperty(*this, setterKind)) {
llvm::Value *setPropertyFn =
CGM.getObjCRuntime().GetPropertySetFunction();
if (!setPropertyFn) {
CGM.ErrorUnsupported(propImpl, "Obj-C setter requiring atomic copy");
return;
}
// Emit objc_setProperty((id) self, _cmd, offset, arg,
// <is-atomic>, <is-copy>).
llvm::Value *cmd =
Builder.CreateLoad(LocalDeclMap[setterMethod->getCmdDecl()]);
llvm::Value *self =
Builder.CreateBitCast(LoadObjCSelf(), VoidPtrTy);
llvm::Value *ivarOffset =
EmitIvarOffset(classImpl->getClassInterface(), ivar);
llvm::Value *arg = LocalDeclMap[*setterMethod->param_begin()];
arg = Builder.CreateBitCast(Builder.CreateLoad(arg, "arg"), VoidPtrTy);
CallArgList args;
args.add(RValue::get(self), getContext().getObjCIdType());
args.add(RValue::get(cmd), getContext().getObjCSelType());
args.add(RValue::get(ivarOffset), getContext().getPointerDiffType());
args.add(RValue::get(arg), getContext().getObjCIdType());
args.add(RValue::get(Builder.getInt1(isAtomic)), getContext().BoolTy);
args.add(RValue::get(Builder.getInt1(isCopy)), getContext().BoolTy);
// FIXME: We shouldn't need to get the function info here, the runtime
// already should have computed it to build the function.
EmitCall(getTypes().getFunctionInfo(getContext().VoidTy, args,
FunctionType::ExtInfo()),
setPropertyFn, ReturnValueSlot(), args);
return;
}
// If the property is atomic but has ARC or GC qualification, we
// must use the expression expansion. This isn't actually right for
// ARC strong, but we shouldn't actually get here for ARC strong,
// which should always end up using setProperty.
if (isAtomic &&
(ivar->getType().hasNonTrivialObjCLifetime() ||
(getLangOptions().getGCMode() &&
getContext().getObjCGCAttrKind(ivar->getType())))) {
// fallthrough
// If the property is atomic and can be implicitly performed
// atomically with an assignment, do so.
} else if (isAtomic && isAssignmentImplicitlyAtomic(*this, ivar)) {
llvm::Value *argAddr = LocalDeclMap[*setterMethod->param_begin()];
LValue ivarLValue =
EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), ivar, /*quals*/ 0);
llvm::Value *ivarAddr = ivarLValue.getAddress();
// If necessary, use a non-aggregate type.
llvm::Type *eltType =
cast<llvm::PointerType>(ivarAddr->getType())->getElementType();
if (eltType->isAggregateType()) {
eltType = llvm::Type::getIntNTy(getLLVMContext(),
getContext().getTypeSize(ivar->getType()));
}
// Cast both arguments to the chosen operation type.
argAddr = Builder.CreateBitCast(argAddr, eltType->getPointerTo());
ivarAddr = Builder.CreateBitCast(ivarAddr, eltType->getPointerTo());
// Emit a single store.
// TODO: this should be a 'store atomic unordered'.
Builder.CreateStore(Builder.CreateLoad(argAddr), ivarAddr);
return;
// Otherwise, if the property is atomic, try to use the runtime's
// atomic-store-struct routine.
} else if (isAtomic && CGM.getObjCRuntime().GetSetStructFunction()) {
GenerateObjCAtomicSetterBody(setterMethod, ivar);
return;
}
// Otherwise, fake up some ASTs and emit a normal assignment.
ValueDecl *selfDecl = setterMethod->getSelfDecl();
DeclRefExpr self(selfDecl, selfDecl->getType(), VK_LValue, SourceLocation());
ImplicitCastExpr selfLoad(ImplicitCastExpr::OnStack,
selfDecl->getType(), CK_LValueToRValue, &self,
VK_RValue);
ObjCIvarRefExpr ivarRef(ivar, ivar->getType().getNonReferenceType(),
SourceLocation(), &selfLoad, true, true);
ParmVarDecl *argDecl = *setterMethod->param_begin();
QualType argType = argDecl->getType().getNonReferenceType();
DeclRefExpr arg(argDecl, argType, VK_LValue, SourceLocation());
ImplicitCastExpr argLoad(ImplicitCastExpr::OnStack,
argType.getUnqualifiedType(), CK_LValueToRValue,
&arg, VK_RValue);
// The property type can differ from the ivar type in some situations with
// Objective-C pointer types, we can always bit cast the RHS in these cases.
// The following absurdity is just to ensure well-formed IR.
CastKind argCK = CK_NoOp;
if (ivarRef.getType()->isObjCObjectPointerType()) {
if (argLoad.getType()->isObjCObjectPointerType())
argCK = CK_BitCast;
else if (argLoad.getType()->isBlockPointerType())
argCK = CK_BlockPointerToObjCPointerCast;
else
argCK = CK_CPointerToObjCPointerCast;
} else if (ivarRef.getType()->isBlockPointerType()) {
if (argLoad.getType()->isBlockPointerType())
argCK = CK_BitCast;
else
argCK = CK_AnyPointerToBlockPointerCast;
} else if (ivarRef.getType()->isPointerType()) {
argCK = CK_BitCast;
}
ImplicitCastExpr argCast(ImplicitCastExpr::OnStack,
ivarRef.getType(), argCK, &argLoad,
VK_RValue);
Expr *finalArg = &argLoad;
if (!getContext().hasSameUnqualifiedType(ivarRef.getType(),
argLoad.getType()))
finalArg = &argCast;
BinaryOperator assign(&ivarRef, finalArg, BO_Assign,
ivarRef.getType(), VK_RValue, OK_Ordinary,
SourceLocation());
EmitStmt(&assign);
}
/// GenerateObjCSetter - Generate an Objective-C property setter
@ -598,136 +791,12 @@ IvarAssignHasTrvialAssignment(const ObjCPropertyImplDecl *PID,
/// is illegal within a category.
void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP,
const ObjCPropertyImplDecl *PID) {
ObjCIvarDecl *Ivar = PID->getPropertyIvarDecl();
const ObjCPropertyDecl *PD = PID->getPropertyDecl();
ObjCMethodDecl *OMD = PD->getSetterMethodDecl();
assert(OMD && "Invalid call to generate setter (empty method)");
StartObjCMethod(OMD, IMP->getClassInterface(), PID->getLocStart());
const llvm::Triple &Triple = getContext().getTargetInfo().getTriple();
QualType IVART = Ivar->getType();
bool IsCopy = PD->getSetterKind() == ObjCPropertyDecl::Copy;
bool IsAtomic =
!(PD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic);
// Determine if we should use an objc_setProperty call for
// this. Properties with 'copy' semantics always use it, as do
// non-atomic properties with 'release' semantics as long as we are
// not in gc-only mode.
if (IsCopy ||
(CGM.getLangOptions().getGCMode() != LangOptions::GCOnly &&
PD->getSetterKind() == ObjCPropertyDecl::Retain)) {
llvm::Value *SetPropertyFn =
CGM.getObjCRuntime().GetPropertySetFunction();
if (!SetPropertyFn) {
CGM.ErrorUnsupported(PID, "Obj-C getter requiring atomic copy");
FinishFunction();
return;
}
// Emit objc_setProperty((id) self, _cmd, offset, arg,
// <is-atomic>, <is-copy>).
// FIXME: Can't this be simpler? This might even be worse than the
// corresponding gcc code.
CodeGenTypes &Types = CGM.getTypes();
ValueDecl *Cmd = OMD->getCmdDecl();
llvm::Value *CmdVal = Builder.CreateLoad(LocalDeclMap[Cmd], "cmd");
QualType IdTy = getContext().getObjCIdType();
llvm::Value *SelfAsId =
Builder.CreateBitCast(LoadObjCSelf(), Types.ConvertType(IdTy));
llvm::Value *Offset = EmitIvarOffset(IMP->getClassInterface(), Ivar);
llvm::Value *Arg = LocalDeclMap[*OMD->param_begin()];
llvm::Value *ArgAsId =
Builder.CreateBitCast(Builder.CreateLoad(Arg, "arg"),
Types.ConvertType(IdTy));
llvm::Value *True =
llvm::ConstantInt::get(Types.ConvertType(getContext().BoolTy), 1);
llvm::Value *False =
llvm::ConstantInt::get(Types.ConvertType(getContext().BoolTy), 0);
CallArgList Args;
Args.add(RValue::get(SelfAsId), IdTy);
Args.add(RValue::get(CmdVal), Cmd->getType());
Args.add(RValue::get(Offset), getContext().getPointerDiffType());
Args.add(RValue::get(ArgAsId), IdTy);
Args.add(RValue::get(IsAtomic ? True : False), getContext().BoolTy);
Args.add(RValue::get(IsCopy ? True : False), getContext().BoolTy);
// FIXME: We shouldn't need to get the function info here, the runtime
// already should have computed it to build the function.
EmitCall(Types.getFunctionInfo(getContext().VoidTy, Args,
FunctionType::ExtInfo()),
SetPropertyFn,
ReturnValueSlot(), Args);
} else if (IsAtomic && hasAggregateLLVMType(IVART) &&
!IVART->isAnyComplexType() &&
IvarAssignHasTrvialAssignment(PID, IVART) &&
((Triple.getArch() == llvm::Triple::x86 &&
(getContext().getTypeSizeInChars(IVART)
> CharUnits::fromQuantity(4))) ||
(Triple.getArch() == llvm::Triple::x86_64 &&
(getContext().getTypeSizeInChars(IVART)
> CharUnits::fromQuantity(8))))
&& CGM.getObjCRuntime().GetSetStructFunction()) {
// objc_copyStruct (&structIvar, &Arg,
// sizeof (struct something), true, false);
GenerateObjCAtomicSetterBody(OMD, Ivar);
} else if (PID->getSetterCXXAssignment()) {
EmitIgnoredExpr(PID->getSetterCXXAssignment());
} else {
if (IsAtomic &&
IVART->isScalarType() &&
(Triple.getArch() == llvm::Triple::arm ||
Triple.getArch() == llvm::Triple::thumb) &&
(getContext().getTypeSizeInChars(IVART)
> CharUnits::fromQuantity(4)) &&
CGM.getObjCRuntime().GetGetStructFunction()) {
GenerateObjCAtomicSetterBody(OMD, Ivar);
}
else if (IsAtomic &&
(IVART->isScalarType() && !IVART->isRealFloatingType()) &&
Triple.getArch() == llvm::Triple::x86 &&
(getContext().getTypeSizeInChars(IVART)
> CharUnits::fromQuantity(4)) &&
CGM.getObjCRuntime().GetGetStructFunction()) {
GenerateObjCAtomicSetterBody(OMD, Ivar);
}
else if (IsAtomic &&
(IVART->isScalarType() && !IVART->isRealFloatingType()) &&
Triple.getArch() == llvm::Triple::x86_64 &&
(getContext().getTypeSizeInChars(IVART)
> CharUnits::fromQuantity(8)) &&
CGM.getObjCRuntime().GetGetStructFunction()) {
GenerateObjCAtomicSetterBody(OMD, Ivar);
}
else {
// FIXME: Find a clean way to avoid AST node creation.
SourceLocation Loc = PID->getLocStart();
ValueDecl *Self = OMD->getSelfDecl();
ObjCIvarDecl *Ivar = PID->getPropertyIvarDecl();
DeclRefExpr Base(Self, Self->getType(), VK_RValue, Loc);
ParmVarDecl *ArgDecl = *OMD->param_begin();
QualType T = ArgDecl->getType();
if (T->isReferenceType())
T = cast<ReferenceType>(T)->getPointeeType();
DeclRefExpr Arg(ArgDecl, T, VK_LValue, Loc);
ObjCIvarRefExpr IvarRef(Ivar, Ivar->getType(), Loc, &Base, true, true);
// The property type can differ from the ivar type in some situations with
// Objective-C pointer types, we can always bit cast the RHS in these cases.
if (getContext().getCanonicalType(Ivar->getType()) !=
getContext().getCanonicalType(ArgDecl->getType())) {
ImplicitCastExpr ArgCasted(ImplicitCastExpr::OnStack,
Ivar->getType(), CK_BitCast, &Arg,
VK_RValue);
BinaryOperator Assign(&IvarRef, &ArgCasted, BO_Assign,
Ivar->getType(), VK_RValue, OK_Ordinary, Loc);
EmitStmt(&Assign);
} else {
BinaryOperator Assign(&IvarRef, &Arg, BO_Assign,
Ivar->getType(), VK_RValue, OK_Ordinary, Loc);
EmitStmt(&Assign);
}
}
}
generateObjCSetterBody(IMP, PID);
FinishFunction();
}

Просмотреть файл

@ -1213,6 +1213,8 @@ public:
/// for the given property.
void GenerateObjCSetter(ObjCImplementationDecl *IMP,
const ObjCPropertyImplDecl *PID);
void generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
const ObjCPropertyImplDecl *propImpl);
bool IndirectObjCSetterArg(const CGFunctionInfo &FI);
bool IvarTypeWithAggrGCObjects(QualType Ty);

Просмотреть файл

@ -129,8 +129,12 @@ namespace CodeGen {
/// The width of a pointer into the generic address space.
unsigned char PointerWidthInBits;
/// The alignment of a pointer into the generic address space.
/// The size and alignment of a pointer into the generic address
/// space.
union {
unsigned char PointerAlignInBytes;
unsigned char PointerSizeInBytes;
};
};
struct RREntrypoints {