forked from OSchip/llvm-project
Document the existing objc_precise_lifetime attribute.
Introduce and document a new objc_returns_inner_pointer attribute, and consume it by performing a retain+autorelease on message receivers when they're not immediately loaded from an object with precise lifetime. llvm-svn: 135764
This commit is contained in:
parent
969c32acb2
commit
cf1667022f
|
@ -1315,6 +1315,39 @@ and only if the value is not demonstrably already retained.</p>
|
|||
<p>The complete optimization rules are quite complicated, but it would
|
||||
still be useful to document them here.</p>
|
||||
|
||||
<div id="optimization.precise">
|
||||
<h1>Precise lifetime semantics</h1>
|
||||
|
||||
<p>In general, ARC maintains an invariant that a retainable object
|
||||
pointer held in a <tt>__strong</tt> object will be retained for the
|
||||
full formal lifetime of the object. Objects subject to this invariant
|
||||
have <span class="term">precise lifetime semantics</span>.</p>
|
||||
|
||||
<p>By default, local variables of automatic storage duration do not
|
||||
have precise lifetime semantics. Such objects are simply strong
|
||||
references which hold values of retainable object pointer type, and
|
||||
these values are still fully subject to the optimizations on values
|
||||
under local control.</p>
|
||||
|
||||
<div class="rationale"><p>Rationale: applying these precise-lifetime
|
||||
semantics strictly would be prohibitive. Many useful optimizations
|
||||
that might theoretically decrease the lifetime of an object would be
|
||||
rendered impossible. Essentially, it promises too much.</p></div>
|
||||
|
||||
<p>A local variable of retainable object owner type and automatic
|
||||
storage duration may be annotated with the <tt>objc_precise_lifetime</tt>
|
||||
attribute to indicate that it should be considered to be an object
|
||||
with precise lifetime semantics.</p>
|
||||
|
||||
<div class="rationale"><p>Rationale: nonetheless, it is sometimes
|
||||
useful to be able to force an object to be released at a precise time,
|
||||
even if that object does not appear to be used. This is likely to be
|
||||
uncommon enough that the syntactic weight of explicitly requesting
|
||||
these semantics will not be burdensome, and may even make the code
|
||||
clearer.</p></div>
|
||||
|
||||
</div> <!-- optimization.precise -->
|
||||
|
||||
</div>
|
||||
|
||||
<div id="misc">
|
||||
|
@ -1562,6 +1595,56 @@ from exceptions.</p></div>
|
|||
|
||||
</div> <!-- misc.exceptions -->
|
||||
|
||||
<div id="misc.interior">
|
||||
<h1>Interior pointers</h1>
|
||||
|
||||
<p>An Objective-C method returning a non-retainable pointer may be
|
||||
annotated with the <tt>objc_returns_inner_pointer</tt> attribute to
|
||||
indicate that it returns a handle to the internal data of an object,
|
||||
and that this reference will be invalidated if the object is
|
||||
destroyed. When such a message is sent to an object, the object's
|
||||
lifetime will be extended until at least the earliest of:</p>
|
||||
|
||||
<ul>
|
||||
<li>the last use of the returned pointer, or any pointer derived from
|
||||
it, in the calling function or</li>
|
||||
<li>the autorelease pool is restored to a previous state.</li>
|
||||
</ul>
|
||||
|
||||
<div class="rationale"><p>Rationale: not all memory and resources are
|
||||
managed with reference counts; it is common for objects to manage
|
||||
private resources in their own, private way. Typically these
|
||||
resources are completely encapsulated within the object, but some
|
||||
classes offer their users direct access for efficiency. If ARC is not
|
||||
aware of methods that return such <q>interior</q> pointers, its
|
||||
optimizations can cause the owning object to be reclaimed too soon.
|
||||
This attribute informs ARC that it must tread lightly.</p>
|
||||
|
||||
<p>The extension rules are somewhat intentionally vague. The
|
||||
autorelease pool limit is there to permit a simple implementation to
|
||||
simply retain and autorelease the receiver. The other limit permits
|
||||
some amount of optimization. The phrase <q>derived from</q> is
|
||||
intended to encompass the results both of pointer transformations,
|
||||
such as casts and arithmetic, and of loading from such derived
|
||||
pointers; furthermore, it applies whether or not such derivations are
|
||||
applied directly in the calling code or by other utility code (for
|
||||
example, the C library routine <tt>strchr</tt>). However, the
|
||||
implementation never need account for uses after a return from the
|
||||
code which calls the method returning an interior pointer.</p></div>
|
||||
|
||||
<p>As an exception, no extension is required if the receiver is loaded
|
||||
directly from a <tt>__strong</tt> object
|
||||
with <a href="#optimization.precise">precise lifetime semantics</a>.</p>
|
||||
|
||||
<div class="rationale"><p>Rationale: implicit autoreleases carry the
|
||||
risk of significantly inflating memory use, so it's important to
|
||||
provide users a way of avoiding these autoreleases. Tying this to
|
||||
precise lifetime semantics is ideal, as for local variables this
|
||||
requires a very explicit annotation, which allows ARC to trust the
|
||||
user with good cheer.</p></div>
|
||||
|
||||
</div> <!-- misc.interior -->
|
||||
|
||||
</div> <!-- misc -->
|
||||
|
||||
<div id="runtime">
|
||||
|
|
|
@ -405,6 +405,11 @@ def ObjCPreciseLifetime : Attr {
|
|||
let Subjects = [Var];
|
||||
}
|
||||
|
||||
def ObjCReturnsInnerPointer : Attr {
|
||||
let Spellings = ["objc_returns_inner_pointer"];
|
||||
let Subjects = [ObjCMethod];
|
||||
}
|
||||
|
||||
def Overloadable : Attr {
|
||||
let Spellings = ["overloadable"];
|
||||
}
|
||||
|
|
|
@ -1413,7 +1413,7 @@ def err_attribute_overloadable_no_prototype : Error<
|
|||
"'overloadable' function %0 must have a prototype">;
|
||||
def warn_ns_attribute_wrong_return_type : Warning<
|
||||
"%0 attribute only applies to %select{functions|methods}1 that "
|
||||
"return %select{an Objective-C object|a pointer}2">;
|
||||
"return %select{an Objective-C object|a pointer|a non-retainable pointer}2">;
|
||||
def warn_ns_attribute_wrong_parameter_type : Warning<
|
||||
"%0 attribute only applies to %select{Objective-C object|pointer}1 "
|
||||
"parameters">;
|
||||
|
|
|
@ -215,6 +215,7 @@ public:
|
|||
AT_objc_method_family,
|
||||
AT_objc_ownership, // Clang-specific.
|
||||
AT_objc_precise_lifetime, // Clang-specific.
|
||||
AT_objc_returns_inner_pointer, // Clang-specific.
|
||||
AT_opencl_image_access, // OpenCL-specific.
|
||||
AT_opencl_kernel_function, // OpenCL-specific.
|
||||
AT_overloadable, // Clang-specific.
|
||||
|
|
|
@ -80,6 +80,53 @@ static RValue AdjustRelatedResultType(CodeGenFunction &CGF,
|
|||
CGF.ConvertType(E->getType())));
|
||||
}
|
||||
|
||||
/// Decide whether to extend the lifetime of the receiver of a
|
||||
/// returns-inner-pointer message.
|
||||
static bool
|
||||
shouldExtendReceiverForInnerPointerMessage(const ObjCMessageExpr *message) {
|
||||
switch (message->getReceiverKind()) {
|
||||
|
||||
// For a normal instance message, we should extend unless the
|
||||
// receiver is loaded from a variable with precise lifetime.
|
||||
case ObjCMessageExpr::Instance: {
|
||||
const Expr *receiver = message->getInstanceReceiver();
|
||||
const ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(receiver);
|
||||
if (!ice || ice->getCastKind() != CK_LValueToRValue) return true;
|
||||
receiver = ice->getSubExpr()->IgnoreParens();
|
||||
|
||||
// Only __strong variables.
|
||||
if (receiver->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
|
||||
return true;
|
||||
|
||||
// All ivars and fields have precise lifetime.
|
||||
if (isa<MemberExpr>(receiver) || isa<ObjCIvarRefExpr>(receiver))
|
||||
return false;
|
||||
|
||||
// Otherwise, check for variables.
|
||||
const DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(ice->getSubExpr());
|
||||
if (!declRef) return true;
|
||||
const VarDecl *var = dyn_cast<VarDecl>(declRef->getDecl());
|
||||
if (!var) return true;
|
||||
|
||||
// All variables have precise lifetime except local variables with
|
||||
// automatic storage duration that aren't specially marked.
|
||||
return (var->hasLocalStorage() &&
|
||||
!var->hasAttr<ObjCPreciseLifetimeAttr>());
|
||||
}
|
||||
|
||||
case ObjCMessageExpr::Class:
|
||||
case ObjCMessageExpr::SuperClass:
|
||||
// It's never necessary for class objects.
|
||||
return false;
|
||||
|
||||
case ObjCMessageExpr::SuperInstance:
|
||||
// We generally assume that 'self' lives throughout a method call.
|
||||
return false;
|
||||
}
|
||||
|
||||
llvm_unreachable("invalid receiver kind");
|
||||
}
|
||||
|
||||
RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
||||
ReturnValueSlot Return) {
|
||||
// Only the lookup mechanism and first two arguments of the method
|
||||
|
@ -88,6 +135,8 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
|
||||
bool isDelegateInit = E->isDelegateInitCall();
|
||||
|
||||
const ObjCMethodDecl *method = E->getMethodDecl();
|
||||
|
||||
// We don't retain the receiver in delegate init calls, and this is
|
||||
// safe because the receiver value is always loaded from 'self',
|
||||
// which we zero out. We don't want to Block_copy block receivers,
|
||||
|
@ -95,8 +144,8 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
bool retainSelf =
|
||||
(!isDelegateInit &&
|
||||
CGM.getLangOptions().ObjCAutoRefCount &&
|
||||
E->getMethodDecl() &&
|
||||
E->getMethodDecl()->hasAttr<NSConsumesSelfAttr>());
|
||||
method &&
|
||||
method->hasAttr<NSConsumesSelfAttr>());
|
||||
|
||||
CGObjCRuntime &Runtime = CGM.getObjCRuntime();
|
||||
bool isSuperMessage = false;
|
||||
|
@ -112,8 +161,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
TryEmitResult ter = tryEmitARCRetainScalarExpr(*this,
|
||||
E->getInstanceReceiver());
|
||||
Receiver = ter.getPointer();
|
||||
if (!ter.getInt())
|
||||
Receiver = EmitARCRetainNonBlock(Receiver);
|
||||
if (ter.getInt()) retainSelf = false;
|
||||
} else
|
||||
Receiver = EmitScalarExpr(E->getInstanceReceiver());
|
||||
break;
|
||||
|
@ -126,9 +174,6 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
assert(OID && "Invalid Objective-C class message send");
|
||||
Receiver = Runtime.GetClass(Builder, OID);
|
||||
isClassMessage = true;
|
||||
|
||||
if (retainSelf)
|
||||
Receiver = EmitARCRetainNonBlock(Receiver);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -136,9 +181,6 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
ReceiverType = E->getSuperType();
|
||||
Receiver = LoadObjCSelf();
|
||||
isSuperMessage = true;
|
||||
|
||||
if (retainSelf)
|
||||
Receiver = EmitARCRetainNonBlock(Receiver);
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::SuperClass:
|
||||
|
@ -146,17 +188,25 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
Receiver = LoadObjCSelf();
|
||||
isSuperMessage = true;
|
||||
isClassMessage = true;
|
||||
|
||||
if (retainSelf)
|
||||
Receiver = EmitARCRetainNonBlock(Receiver);
|
||||
break;
|
||||
}
|
||||
|
||||
if (retainSelf)
|
||||
Receiver = EmitARCRetainNonBlock(Receiver);
|
||||
|
||||
// In ARC, we sometimes want to "extend the lifetime"
|
||||
// (i.e. retain+autorelease) of receivers of returns-inner-pointer
|
||||
// messages.
|
||||
if (getLangOptions().ObjCAutoRefCount && method &&
|
||||
method->hasAttr<ObjCReturnsInnerPointerAttr>() &&
|
||||
shouldExtendReceiverForInnerPointerMessage(E))
|
||||
Receiver = EmitARCRetainAutorelease(ReceiverType, Receiver);
|
||||
|
||||
QualType ResultType =
|
||||
E->getMethodDecl() ? E->getMethodDecl()->getResultType() : E->getType();
|
||||
method ? method->getResultType() : E->getType();
|
||||
|
||||
CallArgList Args;
|
||||
EmitCallArgs(Args, E->getMethodDecl(), E->arg_begin(), E->arg_end());
|
||||
EmitCallArgs(Args, method, E->arg_begin(), E->arg_end());
|
||||
|
||||
// For delegate init calls in ARC, do an unsafe store of null into
|
||||
// self. This represents the call taking direct ownership of that
|
||||
|
@ -189,12 +239,12 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
Receiver,
|
||||
isClassMessage,
|
||||
Args,
|
||||
E->getMethodDecl());
|
||||
method);
|
||||
} else {
|
||||
result = Runtime.GenerateMessageSend(*this, Return, ResultType,
|
||||
E->getSelector(),
|
||||
Receiver, Args, OID,
|
||||
E->getMethodDecl());
|
||||
method);
|
||||
}
|
||||
|
||||
// For delegate init calls in ARC, implicitly store the result of
|
||||
|
@ -213,7 +263,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
|
|||
Builder.CreateStore(newSelf, selfAddr);
|
||||
}
|
||||
|
||||
return AdjustRelatedResultType(*this, E, E->getMethodDecl(), result);
|
||||
return AdjustRelatedResultType(*this, E, method, result);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -163,6 +163,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
|
|||
.Case("vec_type_hint", IgnoredAttribute)
|
||||
.Case("objc_exception", AT_objc_exception)
|
||||
.Case("objc_method_family", AT_objc_method_family)
|
||||
.Case("objc_returns_inner_pointer", AT_objc_returns_inner_pointer)
|
||||
.Case("ext_vector_type", AT_ext_vector_type)
|
||||
.Case("neon_vector_type", AT_neon_vector_type)
|
||||
.Case("neon_polyvector_type", AT_neon_polyvector_type)
|
||||
|
|
|
@ -2767,6 +2767,33 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
|
|||
};
|
||||
}
|
||||
|
||||
static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D,
|
||||
const AttributeList &attr) {
|
||||
SourceLocation loc = attr.getLoc();
|
||||
|
||||
ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(D);
|
||||
|
||||
if (!isa<ObjCMethodDecl>(method)) {
|
||||
S.Diag(method->getLocStart(), diag::err_attribute_wrong_decl_type)
|
||||
<< SourceRange(loc, loc) << attr.getName() << 13 /* methods */;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that the method returns a normal pointer.
|
||||
QualType resultType = method->getResultType();
|
||||
if (!resultType->isPointerType() || resultType->isObjCRetainableType()) {
|
||||
S.Diag(method->getLocStart(), diag::warn_ns_attribute_wrong_return_type)
|
||||
<< SourceRange(loc)
|
||||
<< attr.getName() << /*method*/ 1 << /*non-retainable pointer*/ 2;
|
||||
|
||||
// Drop the attribute.
|
||||
return;
|
||||
}
|
||||
|
||||
method->addAttr(
|
||||
::new (S.Context) ObjCReturnsInnerPointerAttr(loc, S.Context));
|
||||
}
|
||||
|
||||
static void handleObjCOwnershipAttr(Sema &S, Decl *D,
|
||||
const AttributeList &Attr) {
|
||||
if (hasDeclarator(D)) return;
|
||||
|
@ -2969,6 +2996,9 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
|
|||
case AttributeList::AT_objc_precise_lifetime:
|
||||
handleObjCPreciseLifetimeAttr(S, D, Attr); break;
|
||||
|
||||
case AttributeList::AT_objc_returns_inner_pointer:
|
||||
handleObjCReturnsInnerPointerAttr(S, D, Attr); break;
|
||||
|
||||
// Checker-specific.
|
||||
case AttributeList::AT_cf_consumed:
|
||||
case AttributeList::AT_ns_consumed: handleNSConsumedAttr (S, D, Attr); break;
|
||||
|
|
|
@ -1616,3 +1616,53 @@ void test56_test(void) {
|
|||
// CHECK-NEXT: [[T5:%.*]] = load i8** [[T4]]
|
||||
// CHECK-NEXT: ret i8* [[T5]]
|
||||
|
||||
// rdar://problem/9821110
|
||||
@interface Test58
|
||||
- (char*) interior __attribute__((objc_returns_inner_pointer));
|
||||
// Should we allow this on properties?
|
||||
@end
|
||||
extern Test58 *test58_helper(void);
|
||||
|
||||
// CHECK: define void @test58a()
|
||||
void test58a(void) {
|
||||
// CHECK: [[T0:%.*]] = call [[TEST58:%.*]]* @test58_helper()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST58]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]])
|
||||
// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST58]]*
|
||||
// CHECK-NEXT: store [[TEST58]]* [[T3]]
|
||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST58]]**
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST58]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutorelease(i8* [[T1]])
|
||||
// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST58]]*
|
||||
// CHECK-NEXT: [[T4:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
|
||||
// CHECK-NEXT: [[T5:%.*]] = bitcast [[TEST58]]* [[T3]] to i8*
|
||||
// CHECK-NEXT: [[T6:%.*]] = call i8* bitcast
|
||||
// CHECK-NEXT: store i8* [[T6]], i8**
|
||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST58]]**
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST58]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release
|
||||
// CHECK-NEXT: ret void
|
||||
Test58 *ptr = test58_helper();
|
||||
char *c = [(ptr) interior];
|
||||
}
|
||||
|
||||
// CHECK: define void @test58b()
|
||||
void test58b(void) {
|
||||
// CHECK: [[T0:%.*]] = call [[TEST58:%.*]]* @test58_helper()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST58]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]])
|
||||
// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST58]]*
|
||||
// CHECK-NEXT: store [[TEST58]]* [[T3]]
|
||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST58]]**
|
||||
// CHECK-NEXT: [[T1:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
|
||||
// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST58]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: [[T3:%.*]] = call i8* bitcast
|
||||
// CHECK-NEXT: store i8* [[T3]], i8**
|
||||
// CHECK-NEXT: [[T0:%.*]] = load [[TEST58]]**
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST58]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind
|
||||
// CHECK-NOT: clang.imprecise_release
|
||||
// CHECK-NEXT: ret void
|
||||
__attribute__((objc_precise_lifetime)) Test58 *ptr = test58_helper();
|
||||
char *c = [ptr interior];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue