Skip to content

Commit

Permalink
Document the existing objc_precise_lifetime attribute.
Browse files Browse the repository at this point in the history
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.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@135764 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rjmccall committed Jul 22, 2011
1 parent 159a7b3 commit dc7c5ad
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 19 deletions.
83 changes: 83 additions & 0 deletions docs/AutomaticReferenceCounting.html
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,39 @@ <h1>Optimization</h1>
<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">
Expand Down Expand Up @@ -1562,6 +1595,56 @@ <h1>Exceptions</h1>

</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">
Expand Down
5 changes: 5 additions & 0 deletions include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
}
Expand Down
2 changes: 1 addition & 1 deletion include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
1 change: 1 addition & 0 deletions include/clang/Sema/AttributeList.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
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.
Expand Down
86 changes: 68 additions & 18 deletions lib/CodeGen/CGObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -88,15 +135,17 @@ 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,
// though.
bool retainSelf =
(!isDelegateInit &&
CGM.getLangOptions().ObjCAutoRefCount &&
E->getMethodDecl() &&
E->getMethodDecl()->hasAttr<NSConsumesSelfAttr>());
method &&
method->hasAttr<NSConsumesSelfAttr>());

CGObjCRuntime &Runtime = CGM.getObjCRuntime();
bool isSuperMessage = false;
Expand All @@ -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;
Expand All @@ -126,37 +174,39 @@ 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;
}

case ObjCMessageExpr::SuperInstance:
ReceiverType = E->getSuperType();
Receiver = LoadObjCSelf();
isSuperMessage = true;

if (retainSelf)
Receiver = EmitARCRetainNonBlock(Receiver);
break;

case ObjCMessageExpr::SuperClass:
ReceiverType = E->getSuperType();
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
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/AttributeList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
50 changes: 50 additions & 0 deletions test/CodeGenObjC/arc.m
Original file line number Diff line number Diff line change
Expand Up @@ -1616,3 +1616,53 @@ @implementation Test57
// 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];
}

0 comments on commit dc7c5ad

Please sign in to comment.