Skip to content

Commit 11d47b3

Browse files
committed
Implement __attribute__((objc_direct)), __attribute__((objc_direct_members))
__attribute__((objc_direct)) is an attribute on methods declaration, and __attribute__((objc_direct_members)) on implementation, categories or extensions. A `direct` property specifier is added (@Property(direct) type name) These attributes / specifiers cause the method to have no associated Objective-C metadata (for the property or the method itself), and the calling convention to be a direct C function call. The symbol for the method has enforced hidden visibility and such direct calls are hence unreachable cross image. An explicit C function must be made if so desired to wrap them. The implicit `self` and `_cmd` arguments are preserved, however to maintain compatibility with the usual `objc_msgSend` semantics, 3 fundamental precautions are taken: 1) for instance methods, `self` is nil-checked. On arm64 backends this typically adds a single instruction (cbz x0, <closest-ret>) to the codegen, for the vast majority of the cases when the return type is a scalar. 2) for class methods, because the class may not be realized/initialized yet, a call to `[self self]` is emitted. When the proper deployment target is used, this is optimized to `objc_opt_self(self)`. However, long term we might want to emit something better that the optimizer can reason about. When inlining kicks in, these calls aren't optimized away as the optimizer has no idea that a single call is really necessary. 3) the calling convention for the `_cmd` argument is changed: the caller leaves the second argument to the call undefined, and the selector is loaded inside the body when it's referenced only. As far as error reporting goes, the compiler refuses: - making any overloads direct, - making an overload of a direct method, - implementations marked as direct when the declaration in the interface isn't (the other way around is allowed, as the direct attribute is inherited from the declaration), - marking methods required for protocol conformance as direct, - messaging an unqualified `id` with a direct method, - forming any @selector() expression with only direct selectors. As warnings: - any inconsistency of direct-related calling convention when @selector() or messaging is used, - forming any @selector() expression with a possibly direct selector. Lastly an `objc_direct_members` attribute is added that can decorate `@implementation` blocks and causes methods only declared there (and in no `@interface`) to be automatically direct. When decorating an `@interface` then all methods and properties declared in this block are marked direct. Radar-ID: rdar://problem/2684889 Differential Revision: https://reviews.llvm.org/D69991 Reviewed-By: John McCall
1 parent 22922bd commit 11d47b3

24 files changed

+1024
-50
lines changed

clang/include/clang/AST/DeclObjC.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext {
410410
/// \return the type for \c self and set \arg selfIsPseudoStrong and
411411
/// \arg selfIsConsumed accordingly.
412412
QualType getSelfType(ASTContext &Context, const ObjCInterfaceDecl *OID,
413-
bool &selfIsPseudoStrong, bool &selfIsConsumed);
413+
bool &selfIsPseudoStrong, bool &selfIsConsumed) const;
414414

415415
ImplicitParamDecl * getSelfDecl() const { return SelfDecl; }
416416
void setSelfDecl(ImplicitParamDecl *SD) { SelfDecl = SD; }
@@ -476,6 +476,9 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext {
476476
ObjCMethodDeclBits.HasSkippedBody = Skipped;
477477
}
478478

479+
/// True if the method is tagged as objc_direct
480+
bool isDirectMethod() const;
481+
479482
/// Returns the property associated with this method's selector.
480483
///
481484
/// Note that even if this particular method is not marked as a property
@@ -757,13 +760,14 @@ class ObjCPropertyDecl : public NamedDecl {
757760
/// property attribute rather than a type qualifier.
758761
OBJC_PR_nullability = 0x1000,
759762
OBJC_PR_null_resettable = 0x2000,
760-
OBJC_PR_class = 0x4000
763+
OBJC_PR_class = 0x4000,
764+
OBJC_PR_direct = 0x8000
761765
// Adding a property should change NumPropertyAttrsBits
762766
};
763767

764768
enum {
765769
/// Number of bits fitting all the property attributes.
766-
NumPropertyAttrsBits = 15
770+
NumPropertyAttrsBits = 16
767771
};
768772

769773
enum SetterKind { Assign, Retain, Copy, Weak };
@@ -886,6 +890,7 @@ class ObjCPropertyDecl : public NamedDecl {
886890

887891
bool isInstanceProperty() const { return !isClassProperty(); }
888892
bool isClassProperty() const { return PropertyAttributes & OBJC_PR_class; }
893+
bool isDirectProperty() const { return PropertyAttributes & OBJC_PR_direct; }
889894

890895
ObjCPropertyQueryKind getQueryKind() const {
891896
return isClassProperty() ? ObjCPropertyQueryKind::OBJC_PR_query_class :

clang/include/clang/Basic/Attr.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,20 @@ def ObjCDesignatedInitializer : Attr {
18711871
let Documentation = [Undocumented];
18721872
}
18731873

1874+
def ObjCDirect : Attr {
1875+
let Spellings = [Clang<"objc_direct">];
1876+
let Subjects = SubjectList<[ObjCMethod], ErrorDiag>;
1877+
let LangOpts = [ObjC];
1878+
let Documentation = [ObjCDirectDocs];
1879+
}
1880+
1881+
def ObjCDirectMembers : Attr {
1882+
let Spellings = [Clang<"objc_direct_members">];
1883+
let Subjects = SubjectList<[ObjCImpl, ObjCCategory], ErrorDiag>;
1884+
let LangOpts = [ObjC];
1885+
let Documentation = [ObjCDirectMembersDocs];
1886+
}
1887+
18741888
def ObjCRuntimeName : Attr {
18751889
let Spellings = [Clang<"objc_runtime_name">];
18761890
let Subjects = SubjectList<[ObjCInterface, ObjCProtocol], ErrorDiag>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4122,6 +4122,104 @@ overheads associated with defining and calling such a method.
41224122
}];
41234123
}
41244124

4125+
def ObjCDirectDocs : Documentation {
4126+
let Category = DocCatDecl;
4127+
let Content = [{
4128+
The ``objc_direct`` attribute can be used to mark an Objective-C method as
4129+
being *direct*. A direct method is treated statically like an ordinary method,
4130+
but dynamically it behaves more like a C function. This lowers some of the costs
4131+
associated with the method but also sacrifices some of the ordinary capabilities
4132+
of Objective-C methods.
4133+
4134+
A message send of a direct method calls the implementation directly, as if it
4135+
were a C function, rather than using ordinary Objective-C method dispatch. This
4136+
is substantially faster and potentially allows the implementation to be inlined,
4137+
but it also means the method cannot be overridden in subclasses or replaced
4138+
dynamically, as ordinary Objective-C methods can.
4139+
4140+
Furthermore, a direct method is not listed in the class's method lists. This
4141+
substantially reduces the code-size overhead of the method but also means it
4142+
cannot be called dynamically using ordinary Objective-C method dispatch at all;
4143+
in particular, this means that it cannot override a superclass method or satisfy
4144+
a protocol requirement.
4145+
4146+
Because a direct method cannot be overridden, it is an error to perform
4147+
a ``super`` message send of one.
4148+
4149+
Although a message send of a direct method causes the method to be called
4150+
directly as if it were a C function, it still obeys Objective-C semantics in other
4151+
ways:
4152+
4153+
- If the receiver is ``nil``, the message send does nothing and returns the zero value
4154+
for the return type.
4155+
4156+
- A message send of a direct class method will cause the class to be initialized,
4157+
including calling the ``+initialize`` method if present.
4158+
4159+
- The implicit ``_cmd`` parameter containing the method's selector is still defined.
4160+
In order to minimize code-size costs, the implementation will not emit a reference
4161+
to the selector if the parameter is unused within the method.
4162+
4163+
Symbols for direct method implementations are implicitly given hidden
4164+
visibility, meaning that they can only be called within the same linkage unit.
4165+
4166+
It is an error to do any of the following:
4167+
4168+
- declare a direct method in a protocol,
4169+
- declare an override of a direct method with a method in a subclass,
4170+
- declare an override of a non-direct method with a direct method in a subclass,
4171+
- declare a method with different directness in different class interfaces, or
4172+
- implement a non-direct method (as declared in any class interface) with a direct method.
4173+
4174+
If any of these rules would be violated if every method defined in an
4175+
``@implementation`` within a single linkage unit were declared in an
4176+
appropriate class interface, the program is ill-formed with no diagnostic
4177+
required. If a violation of this rule is not diagnosed, behavior remains
4178+
well-defined; this paragraph is simply reserving the right to diagnose such
4179+
conflicts in the future, not to treat them as undefined behavior.
4180+
4181+
Additionally, Clang will warn about any ``@selector`` expression that
4182+
names a selector that is only known to be used for direct methods.
4183+
4184+
For the purpose of these rules, a "class interface" includes a class's primary
4185+
``@interface`` block, its class extensions, its categories, its declared protocols,
4186+
and all the class interfaces of its superclasses.
4187+
4188+
An Objective-C property can be declared with the ``direct`` property
4189+
attribute. If a direct property declaration causes an implicit declaration of
4190+
a getter or setter method (that is, if the given method is not explicitly
4191+
declared elsewhere), the method is declared to be direct.
4192+
4193+
Some programmers may wish to make many methods direct at once. In order
4194+
to simplify this, the ``objc_direct_members`` attribute is provided; see its
4195+
documentation for more information.
4196+
}];
4197+
}
4198+
4199+
def ObjCDirectMembersDocs : Documentation {
4200+
let Category = DocCatDecl;
4201+
let Content = [{
4202+
The ``objc_direct_members`` attribute can be placed on an Objective-C
4203+
``@interface`` or ``@implementation`` to mark that methods declared
4204+
therein should be considered direct by default. See the documentation
4205+
for ``objc_direct`` for more information about direct methods.
4206+
4207+
When ``objc_direct_members`` is placed on an ``@interface`` block, every
4208+
method in the block is considered to be declared as direct. This includes any
4209+
implicit method declarations introduced by property declarations. If the method
4210+
redeclares a non-direct method, the declaration is ill-formed, exactly as if the
4211+
method was annotated with the ``objc_direct`` attribute. ``objc_direct_members``
4212+
cannot be placed on the primary interface of a class, only on category or class
4213+
extension interfaces.
4214+
4215+
When ``objc_direct_members`` is placed on an ``@implementation`` block,
4216+
methods defined in the block are considered to be declared as direct unless
4217+
they have been previously declared as non-direct in any interface of the class.
4218+
This includes the implicit method definitions introduced by synthesized
4219+
properties, including auto-synthesized properties.
4220+
}];
4221+
}
4222+
41254223
def SelectAnyDocs : Documentation {
41264224
let Category = DocCatDecl;
41274225
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,22 @@ def warn_objc_boxing_invalid_utf8_string : Warning<
10371037
"string is ill-formed as UTF-8 and will become a null %0 when boxed">,
10381038
InGroup<ObjCBoxing>;
10391039

1040+
def err_objc_direct_on_protocol : Error<
1041+
"'objc_direct' attribute cannot be applied to %select{methods|properties}0 "
1042+
"declared in an Objective-C protocol">;
1043+
def err_objc_direct_missing_on_decl : Error<
1044+
"direct method implementation was previously declared not direct">;
1045+
def err_objc_direct_on_override : Error<
1046+
"methods that %select{override superclass methods|implement protocol requirements}0 cannot be direct">;
1047+
def err_objc_override_direct_method : Error<
1048+
"cannot override a method that is declared direct by a superclass">;
1049+
def warn_objc_direct_ignored : Warning<
1050+
"%0 attribute isn't implemented by this Objective-C runtime">,
1051+
InGroup<IgnoredAttributes>;
1052+
def warn_objc_direct_property_ignored : Warning<
1053+
"direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">,
1054+
InGroup<IgnoredAttributes>;
1055+
10401056
def warn_conflicting_overriding_ret_types : Warning<
10411057
"conflicting return type in "
10421058
"declaration of %0%diff{: $ vs $|}1,2">,
@@ -1122,6 +1138,7 @@ def warn_accessor_property_type_mismatch : Warning<
11221138
"type of property %0 does not match type of accessor %1">;
11231139
def note_conv_function_declared_at : Note<"type conversion function declared here">;
11241140
def note_method_declared_at : Note<"method %0 declared here">;
1141+
def note_direct_method_declared_at : Note<"direct method %0 declared here">;
11251142
def note_property_attribute : Note<"property %0 is declared "
11261143
"%select{deprecated|unavailable|partial}1 here">;
11271144
def err_setter_type_void : Error<"type of setter must be void">;
@@ -1363,6 +1380,8 @@ def warn_multiple_selectors: Warning<
13631380
"several methods with selector %0 of mismatched types are found "
13641381
"for the @selector expression">,
13651382
InGroup<SelectorTypeMismatch>, DefaultIgnore;
1383+
def err_direct_selector_expression: Error<
1384+
"@selector expression formed with direct selector %0">;
13661385

13671386
def err_objc_kindof_nonobject : Error<
13681387
"'__kindof' specifier cannot be applied to non-object type %0">;
@@ -1376,6 +1395,12 @@ def err_objc_method_unsupported_param_ret_type : Error<
13761395
def warn_messaging_unqualified_id : Warning<
13771396
"messaging unqualified id">, DefaultIgnore,
13781397
InGroup<DiagGroup<"objc-messaging-id">>;
1398+
def err_messaging_unqualified_id_with_direct_method : Error<
1399+
"messaging unqualified id with a method that is possibly direct">;
1400+
def err_messaging_super_with_direct_method : Error<
1401+
"messaging super with a direct method">;
1402+
def err_messaging_class_with_direct_method : Error<
1403+
"messaging a Class with a method that is possibly direct">;
13791404

13801405
// C++ declarations
13811406
def err_static_assert_expression_is_not_constant : Error<

clang/include/clang/Basic/ObjCRuntime.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,20 @@ class ObjCRuntime {
446446
llvm_unreachable("bad kind");
447447
}
448448

449+
/// Does this runtime supports direct dispatch
450+
bool allowsDirectDispatch() const {
451+
switch (getKind()) {
452+
case FragileMacOSX: return false;
453+
case MacOSX: return true;
454+
case iOS: return true;
455+
case WatchOS: return true;
456+
case GCC: return false;
457+
case GNUstep: return false;
458+
case ObjFW: return false;
459+
}
460+
llvm_unreachable("bad kind");
461+
}
462+
449463
/// Try to parse an Objective-C runtime specification from the given
450464
/// string.
451465
///

clang/include/clang/Sema/DeclSpec.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,8 @@ class ObjCDeclSpec {
833833
DQ_PR_unsafe_unretained = 0x800,
834834
DQ_PR_nullability = 0x1000,
835835
DQ_PR_null_resettable = 0x2000,
836-
DQ_PR_class = 0x4000
836+
DQ_PR_class = 0x4000,
837+
DQ_PR_direct = 0x8000,
837838
};
838839

839840
ObjCDeclSpec()
@@ -903,7 +904,7 @@ class ObjCDeclSpec {
903904
unsigned objcDeclQualifier : 7;
904905

905906
// NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
906-
unsigned PropertyAttributes : 15;
907+
unsigned PropertyAttributes : 16;
907908

908909
unsigned Nullability : 2;
909910

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8969,6 +8969,9 @@ class Sema {
89698969
checkRelatedResultTypeCompatibility(const ObjCMethodDecl *Method,
89708970
const ObjCInterfaceDecl *CurrentClass);
89718971

8972+
void CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
8973+
ObjCMethodDecl *overridden);
8974+
89728975
void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
89738976
ObjCInterfaceDecl *CurrentClass,
89748977
ResultTypeCompatibilityKind RTC);

clang/lib/AST/DeclObjC.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,10 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
823823
Selector(), QualType(), nullptr, nullptr);
824824
}
825825

826+
bool ObjCMethodDecl::isDirectMethod() const {
827+
return hasAttr<ObjCDirectAttr>();
828+
}
829+
826830
bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
827831
return getMethodFamily() == OMF_init &&
828832
hasAttr<ObjCDesignatedInitializerAttr>();
@@ -1077,7 +1081,7 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const {
10771081
QualType ObjCMethodDecl::getSelfType(ASTContext &Context,
10781082
const ObjCInterfaceDecl *OID,
10791083
bool &selfIsPseudoStrong,
1080-
bool &selfIsConsumed) {
1084+
bool &selfIsConsumed) const {
10811085
QualType selfTy;
10821086
selfIsPseudoStrong = false;
10831087
selfIsConsumed = false;

clang/lib/AST/DeclPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
14741474
first = false;
14751475
}
14761476

1477+
if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_direct) {
1478+
Out << (first ? "" : ", ") << "direct";
1479+
first = false;
1480+
}
1481+
14771482
if (PDecl->getPropertyAttributes() &
14781483
ObjCPropertyDecl::OBJC_PR_nonatomic) {
14791484
Out << (first ? "" : ", ") << "nonatomic";

clang/lib/AST/JSONNodeDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,7 @@ void JSONNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
10131013
attributeOnlyIfTrue("unsafe_unretained",
10141014
Attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained);
10151015
attributeOnlyIfTrue("class", Attrs & ObjCPropertyDecl::OBJC_PR_class);
1016+
attributeOnlyIfTrue("direct", Attrs & ObjCPropertyDecl::OBJC_PR_direct);
10161017
attributeOnlyIfTrue("nullability",
10171018
Attrs & ObjCPropertyDecl::OBJC_PR_nullability);
10181019
attributeOnlyIfTrue("null_resettable",

0 commit comments

Comments
 (0)