Skip to content

Commit

Permalink
[OBJC] Add attribute to mark Objective C class as non-lazy
Browse files Browse the repository at this point in the history
A non-lazy class will be initialized eagerly when the Objective-C runtime is
loaded. This is required for certain system classes which have instances allocated in
non-standard ways, such as the classes for blocks and constant strings.
Adding this attribute is essentially equivalent to providing a trivial
+load method but avoids the (fairly small) load-time overheads associated
with defining and calling such a method.

Differential Revision: https://reviews.llvm.org/D56555

llvm-svn: 353116
  • Loading branch information
Joe Daniels committed Feb 4, 2019
1 parent 3062887 commit f7393d2
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 4 deletions.
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Expand Up @@ -1758,6 +1758,13 @@ def ObjCRootClass : InheritableAttr {
let Documentation = [Undocumented];
}

def ObjCNonLazyClass : Attr {
let Spellings = [Clang<"objc_nonlazy_class">];
let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
let LangOpts = [ObjC];
let Documentation = [ObjCNonLazyClassDocs];
}

def ObjCSubclassingRestricted : InheritableAttr {
let Spellings = [Clang<"objc_subclassing_restricted">];
let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Expand Up @@ -3699,6 +3699,19 @@ ensure that this class cannot be subclassed.
}];
}

def ObjCNonLazyClassDocs : Documentation {
let Category = DocCatType;
let Content = [{
This attribute can be added to an Objective-C ``@interface`` declaration to
add the class to the list of non-lazily initialized classes. A non-lazy class
will be initialized eagerly when the Objective-C runtime is loaded. This is
required for certain system classes which have instances allocated in
non-standard ways, such as the classes for blocks and constant strings. Adding
this attribute is essentially equivalent to providing a trivial `+load` method
but avoids the (fairly small) load-time overheads associated with defining and
calling such a method.
}];
}

def SelectAnyDocs : Documentation {
let Category = DocCatType;
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/CodeGen/CGObjCMac.cpp
Expand Up @@ -6261,9 +6261,10 @@ CGObjCNonFragileABIMac::BuildClassObject(const ObjCInterfaceDecl *CI,
return GV;
}

bool
CGObjCNonFragileABIMac::ImplementationIsNonLazy(const ObjCImplDecl *OD) const {
return OD->getClassMethod(GetNullarySelector("load")) != nullptr;
bool CGObjCNonFragileABIMac::ImplementationIsNonLazy(
const ObjCImplDecl *OD) const {
return OD->getClassMethod(GetNullarySelector("load")) != nullptr ||
OD->getClassInterface()->hasAttr<ObjCNonLazyClassAttr>();
}

void CGObjCNonFragileABIMac::GetClassSizeInfo(const ObjCImplementationDecl *OID,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Expand Up @@ -6832,6 +6832,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_ObjCRootClass:
handleSimpleAttribute<ObjCRootClassAttr>(S, D, AL);
break;
case ParsedAttr::AT_ObjCNonLazyClass:
handleSimpleAttribute<ObjCNonLazyClassAttr>(S, D, AL);
break;
case ParsedAttr::AT_ObjCSubclassingRestricted:
handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, AL);
break;
Expand Down
7 changes: 6 additions & 1 deletion clang/test/CodeGenObjC/non-lazy-classes.m
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | \
// RUN: FileCheck %s
// CHECK: @"OBJC_LABEL_NONLAZY_CLASS_$" = private global [1 x {{.*}}] {{.*}}@"OBJC_CLASS_$_A"{{.*}}, section "__DATA,__objc_nlclslist,regular,no_dead_strip", align 8
// CHECK: @"OBJC_LABEL_NONLAZY_CLASS_$" = private global [2 x {{.*}}]{{.*}}@"OBJC_CLASS_$_A"{{.*}},{{.*}}@"OBJC_CLASS_$_D"{{.*}} section "__DATA,__objc_nlclslist,regular,no_dead_strip", align 8
// CHECK: @"OBJC_LABEL_NONLAZY_CATEGORY_$" = private global [1 x {{.*}}] {{.*}}@"\01l_OBJC_$_CATEGORY_A_$_Cat"{{.*}}, section "__DATA,__objc_nlcatlist,regular,no_dead_strip", align 8

@interface A @end
Expand Down Expand Up @@ -30,3 +30,8 @@ -(void) load {
@interface C : A @end
@implementation C
@end

__attribute__((objc_nonlazy_class))
@interface D @end

@implementation D @end
Expand Up @@ -100,6 +100,7 @@
// CHECK-NEXT: ObjCExplicitProtocolImpl (SubjectMatchRule_objc_protocol)
// CHECK-NEXT: ObjCExternallyRetained (SubjectMatchRule_variable_not_is_parameter, SubjectMatchRule_function, SubjectMatchRule_block, SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCMethodFamily (SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCNonLazyClass (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCPreciseLifetime (SubjectMatchRule_variable)
// CHECK-NEXT: ObjCRequiresPropertyDefs (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCRequiresSuper (SubjectMatchRule_objc_method)
Expand Down
34 changes: 34 additions & 0 deletions clang/test/SemaObjC/attr-objc-non-lazy.m
@@ -0,0 +1,34 @@
// RUN: %clang_cc1 -verify -Wno-objc-root-class -fsyntax-only %s

__attribute__((objc_nonlazy_class))
@interface A
@end
@implementation A
@end

__attribute__((objc_nonlazy_class)) int X; // expected-error {{'objc_nonlazy_class' attribute only applies to Objective-C interfaces}}

__attribute__((objc_nonlazy_class()))
@interface B
@end
@implementation B
@end

__attribute__((objc_nonlazy_class("foo"))) // expected-error{{'objc_nonlazy_class' attribute takes no arguments}}
@interface C
@end
@implementation C
@end

__attribute__((objc_nonlazy_class)) // expected-error {{'objc_nonlazy_class' attribute only applies to Objective-C interfaces}}
@protocol B
@end

__attribute__((objc_nonlazy_class)) // expected-error {{'objc_nonlazy_class' attribute only applies to Objective-C interfaces}}
void foo();

@interface E
@end
__attribute__((objc_nonlazy_class))
@implementation E // expected-error {{prefix attribute must be followed by an interface or protocol}}
@end

0 comments on commit f7393d2

Please sign in to comment.