Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement __attribute__((objc_direct)), __attribute__((objc_direct_me… #328

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions clang/include/clang/AST/DeclObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext {
/// \return the type for \c self and set \arg selfIsPseudoStrong and
/// \arg selfIsConsumed accordingly.
QualType getSelfType(ASTContext &Context, const ObjCInterfaceDecl *OID,
bool &selfIsPseudoStrong, bool &selfIsConsumed);
bool &selfIsPseudoStrong, bool &selfIsConsumed) const;

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

/// True if the method is tagged as objc_direct
bool isDirectMethod() const;

/// Returns the property associated with this method's selector.
///
/// Note that even if this particular method is not marked as a property
Expand Down Expand Up @@ -757,13 +760,14 @@ class ObjCPropertyDecl : public NamedDecl {
/// property attribute rather than a type qualifier.
OBJC_PR_nullability = 0x1000,
OBJC_PR_null_resettable = 0x2000,
OBJC_PR_class = 0x4000
OBJC_PR_class = 0x4000,
OBJC_PR_direct = 0x8000
// Adding a property should change NumPropertyAttrsBits
};

enum {
/// Number of bits fitting all the property attributes.
NumPropertyAttrsBits = 15
NumPropertyAttrsBits = 16
};

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

bool isInstanceProperty() const { return !isClassProperty(); }
bool isClassProperty() const { return PropertyAttributes & OBJC_PR_class; }
bool isDirectProperty() const { return PropertyAttributes & OBJC_PR_direct; }

ObjCPropertyQueryKind getQueryKind() const {
return isClassProperty() ? ObjCPropertyQueryKind::OBJC_PR_query_class :
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,20 @@ def ObjCDesignatedInitializer : Attr {
let Documentation = [Undocumented];
}

def ObjCDirect : Attr {
let Spellings = [Clang<"objc_direct">];
let Subjects = SubjectList<[ObjCMethod], ErrorDiag>;
let LangOpts = [ObjC];
let Documentation = [ObjCDirectDocs];
}

def ObjCDirectMembers : Attr {
let Spellings = [Clang<"objc_direct_members">];
let Subjects = SubjectList<[ObjCImpl, ObjCCategory], ErrorDiag>;
let LangOpts = [ObjC];
let Documentation = [ObjCDirectMembersDocs];
}

def ObjCRuntimeName : Attr {
let Spellings = [Clang<"objc_runtime_name">];
let Subjects = SubjectList<[ObjCInterface, ObjCProtocol], ErrorDiag>;
Expand Down
98 changes: 98 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -4122,6 +4122,104 @@ overheads associated with defining and calling such a method.
}];
}

def ObjCDirectDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
The ``objc_direct`` attribute can be used to mark an Objective-C method as
being *direct*. A direct method is treated statically like an ordinary method,
but dynamically it behaves more like a C function. This lowers some of the costs
associated with the method but also sacrifices some of the ordinary capabilities
of Objective-C methods.

A message send of a direct method calls the implementation directly, as if it
were a C function, rather than using ordinary Objective-C method dispatch. This
is substantially faster and potentially allows the implementation to be inlined,
but it also means the method cannot be overridden in subclasses or replaced
dynamically, as ordinary Objective-C methods can.

Furthermore, a direct method is not listed in the class's method lists. This
substantially reduces the code-size overhead of the method but also means it
cannot be called dynamically using ordinary Objective-C method dispatch at all;
in particular, this means that it cannot override a superclass method or satisfy
a protocol requirement.

Because a direct method cannot be overridden, it is an error to perform
a ``super`` message send of one.

Although a message send of a direct method causes the method to be called
directly as if it were a C function, it still obeys Objective-C semantics in other
ways:

- If the receiver is ``nil``, the message send does nothing and returns the zero value
for the return type.

- A message send of a direct class method will cause the class to be initialized,
including calling the ``+initialize`` method if present.

- The implicit ``_cmd`` parameter containing the method's selector is still defined.
In order to minimize code-size costs, the implementation will not emit a reference
to the selector if the parameter is unused within the method.

Symbols for direct method implementations are implicitly given hidden
visibility, meaning that they can only be called within the same linkage unit.

It is an error to do any of the following:

- declare a direct method in a protocol,
- declare an override of a direct method with a method in a subclass,
- declare an override of a non-direct method with a direct method in a subclass,
- declare a method with different directness in different class interfaces, or
- implement a non-direct method (as declared in any class interface) with a direct method.

If any of these rules would be violated if every method defined in an
``@implementation`` within a single linkage unit were declared in an
appropriate class interface, the program is ill-formed with no diagnostic
required. If a violation of this rule is not diagnosed, behavior remains
well-defined; this paragraph is simply reserving the right to diagnose such
conflicts in the future, not to treat them as undefined behavior.

Additionally, Clang will warn about any ``@selector`` expression that
names a selector that is only known to be used for direct methods.

For the purpose of these rules, a "class interface" includes a class's primary
``@interface`` block, its class extensions, its categories, its declared protocols,
and all the class interfaces of its superclasses.

An Objective-C property can be declared with the ``direct`` property
attribute. If a direct property declaration causes an implicit declaration of
a getter or setter method (that is, if the given method is not explicitly
declared elsewhere), the method is declared to be direct.

Some programmers may wish to make many methods direct at once. In order
to simplify this, the ``objc_direct_members`` attribute is provided; see its
documentation for more information.
}];
}

def ObjCDirectMembersDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
The ``objc_direct_members`` attribute can be placed on an Objective-C
``@interface`` or ``@implementation`` to mark that methods declared
therein should be considered direct by default. See the documentation
for ``objc_direct`` for more information about direct methods.

When ``objc_direct_members`` is placed on an ``@interface`` block, every
method in the block is considered to be declared as direct. This includes any
implicit method declarations introduced by property declarations. If the method
redeclares a non-direct method, the declaration is ill-formed, exactly as if the
method was annotated with the ``objc_direct`` attribute. ``objc_direct_members``
cannot be placed on the primary interface of a class, only on category or class
extension interfaces.

When ``objc_direct_members`` is placed on an ``@implementation`` block,
methods defined in the block are considered to be declared as direct unless
they have been previously declared as non-direct in any interface of the class.
This includes the implicit method definitions introduced by synthesized
properties, including auto-synthesized properties.
}];
}

def SelectAnyDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
Expand Down
25 changes: 25 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,22 @@ def warn_objc_boxing_invalid_utf8_string : Warning<
"string is ill-formed as UTF-8 and will become a null %0 when boxed">,
InGroup<ObjCBoxing>;

def err_objc_direct_on_protocol : Error<
"'objc_direct' attribute cannot be applied to %select{methods|properties}0 "
"declared in an Objective-C protocol">;
def err_objc_direct_missing_on_decl : Error<
"direct method implementation was previously declared not direct">;
def err_objc_direct_on_override : Error<
"methods that %select{override superclass methods|implement protocol requirements}0 cannot be direct">;
def err_objc_override_direct_method : Error<
"cannot override a method that is declared direct by a superclass">;
def warn_objc_direct_ignored : Warning<
"%0 attribute isn't implemented by this Objective-C runtime">,
InGroup<IgnoredAttributes>;
def warn_objc_direct_property_ignored : Warning<
"direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">,
InGroup<IgnoredAttributes>;

def warn_conflicting_overriding_ret_types : Warning<
"conflicting return type in "
"declaration of %0%diff{: $ vs $|}1,2">,
Expand Down Expand Up @@ -1122,6 +1138,7 @@ def warn_accessor_property_type_mismatch : Warning<
"type of property %0 does not match type of accessor %1">;
def note_conv_function_declared_at : Note<"type conversion function declared here">;
def note_method_declared_at : Note<"method %0 declared here">;
def note_direct_method_declared_at : Note<"direct method %0 declared here">;
def note_property_attribute : Note<"property %0 is declared "
"%select{deprecated|unavailable|partial}1 here">;
def err_setter_type_void : Error<"type of setter must be void">;
Expand Down Expand Up @@ -1363,6 +1380,8 @@ def warn_multiple_selectors: Warning<
"several methods with selector %0 of mismatched types are found "
"for the @selector expression">,
InGroup<SelectorTypeMismatch>, DefaultIgnore;
def err_direct_selector_expression: Error<
"@selector expression formed with direct selector %0">;

def err_objc_kindof_nonobject : Error<
"'__kindof' specifier cannot be applied to non-object type %0">;
Expand All @@ -1376,6 +1395,12 @@ def err_objc_method_unsupported_param_ret_type : Error<
def warn_messaging_unqualified_id : Warning<
"messaging unqualified id">, DefaultIgnore,
InGroup<DiagGroup<"objc-messaging-id">>;
def err_messaging_unqualified_id_with_direct_method : Error<
"messaging unqualified id with a method that is possibly direct">;
def err_messaging_super_with_direct_method : Error<
"messaging super with a direct method">;
def err_messaging_class_with_direct_method : Error<
"messaging a Class with a method that is possibly direct">;

// C++ declarations
def err_static_assert_expression_is_not_constant : Error<
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Basic/ObjCRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,20 @@ class ObjCRuntime {
llvm_unreachable("bad kind");
}

/// Does this runtime supports direct dispatch
bool allowsDirectDispatch() const {
switch (getKind()) {
case FragileMacOSX: return false;
case MacOSX: return true;
case iOS: return true;
case WatchOS: return true;
case GCC: return false;
case GNUstep: return false;
case ObjFW: return false;
}
llvm_unreachable("bad kind");
}

/// Try to parse an Objective-C runtime specification from the given
/// string.
///
Expand Down
5 changes: 3 additions & 2 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,8 @@ class ObjCDeclSpec {
DQ_PR_unsafe_unretained = 0x800,
DQ_PR_nullability = 0x1000,
DQ_PR_null_resettable = 0x2000,
DQ_PR_class = 0x4000
DQ_PR_class = 0x4000,
DQ_PR_direct = 0x8000,
};

ObjCDeclSpec()
Expand Down Expand Up @@ -903,7 +904,7 @@ class ObjCDeclSpec {
unsigned objcDeclQualifier : 7;

// NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
unsigned PropertyAttributes : 15;
unsigned PropertyAttributes : 16;

unsigned Nullability : 2;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8969,6 +8969,9 @@ class Sema {
checkRelatedResultTypeCompatibility(const ObjCMethodDecl *Method,
const ObjCInterfaceDecl *CurrentClass);

void CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
ObjCMethodDecl *overridden);

void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
ObjCInterfaceDecl *CurrentClass,
ResultTypeCompatibilityKind RTC);
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/AST/DeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,10 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
Selector(), QualType(), nullptr, nullptr);
}

bool ObjCMethodDecl::isDirectMethod() const {
return hasAttr<ObjCDirectAttr>();
}

bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
return getMethodFamily() == OMF_init &&
hasAttr<ObjCDesignatedInitializerAttr>();
Expand Down Expand Up @@ -1077,7 +1081,7 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const {
QualType ObjCMethodDecl::getSelfType(ASTContext &Context,
const ObjCInterfaceDecl *OID,
bool &selfIsPseudoStrong,
bool &selfIsConsumed) {
bool &selfIsConsumed) const {
QualType selfTy;
selfIsPseudoStrong = false;
selfIsConsumed = false;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/DeclPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
first = false;
}

if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_direct) {
Out << (first ? "" : ", ") << "direct";
first = false;
}

if (PDecl->getPropertyAttributes() &
ObjCPropertyDecl::OBJC_PR_nonatomic) {
Out << (first ? "" : ", ") << "nonatomic";
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/JSONNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,7 @@ void JSONNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
attributeOnlyIfTrue("unsafe_unretained",
Attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained);
attributeOnlyIfTrue("class", Attrs & ObjCPropertyDecl::OBJC_PR_class);
attributeOnlyIfTrue("direct", Attrs & ObjCPropertyDecl::OBJC_PR_direct);
attributeOnlyIfTrue("nullability",
Attrs & ObjCPropertyDecl::OBJC_PR_nullability);
attributeOnlyIfTrue("null_resettable",
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1926,6 +1926,8 @@ void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
OS << " unsafe_unretained";
if (Attrs & ObjCPropertyDecl::OBJC_PR_class)
OS << " class";
if (Attrs & ObjCPropertyDecl::OBJC_PR_direct)
OS << " direct";
if (Attrs & ObjCPropertyDecl::OBJC_PR_getter)
dumpDeclRef(D->getGetterMethodDecl(), "getter");
if (Attrs & ObjCPropertyDecl::OBJC_PR_setter)
Expand Down
43 changes: 32 additions & 11 deletions clang/lib/CodeGen/CGObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,20 @@ tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
return None;
}

CodeGen::RValue CGObjCRuntime::GeneratePossiblySpecializedMessageSend(
CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType,
Selector Sel, llvm::Value *Receiver, const CallArgList &Args,
const ObjCInterfaceDecl *OID, const ObjCMethodDecl *Method,
bool isClassMessage) {
if (Optional<llvm::Value *> SpecializedResult =
tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args,
Sel, Method, isClassMessage)) {
return RValue::get(SpecializedResult.getValue());
}
return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID,
Method);
}

/// Instead of '[[MyClass alloc] init]', try to generate
/// 'objc_alloc_init(MyClass)'. This provides a code size improvement on the
/// caller side, as well as the optimized objc_alloc.
Expand Down Expand Up @@ -611,16 +625,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
method);
} else {
// Call runtime methods directly if we can.
if (Optional<llvm::Value *> SpecializedResult =
tryGenerateSpecializedMessageSend(*this, ResultType, Receiver, Args,
E->getSelector(), method,
isClassMessage)) {
result = RValue::get(SpecializedResult.getValue());
} else {
result = Runtime.GenerateMessageSend(*this, Return, ResultType,
E->getSelector(), Receiver, Args,
OID, method);
}
result = Runtime.GeneratePossiblySpecializedMessageSend(
*this, Return, ResultType, E->getSelector(), Receiver, Args, OID,
method, isClassMessage);
}

// For delegate init calls in ARC, implicitly store the result of
Expand Down Expand Up @@ -683,7 +690,13 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD,
llvm::Function *Fn = CGM.getObjCRuntime().GenerateMethod(OMD, CD);

const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD);
CGM.SetInternalFunctionAttributes(OMD, Fn, FI);
if (OMD->isDirectMethod()) {
Fn->setVisibility(llvm::Function::HiddenVisibility);
CGM.SetLLVMFunctionAttributes(OMD, FI, Fn);
CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn);
} else {
CGM.SetInternalFunctionAttributes(OMD, Fn, FI);
}

args.push_back(OMD->getSelfDecl());
args.push_back(OMD->getCmdDecl());
Expand All @@ -696,6 +709,14 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD,
StartFunction(OMD, OMD->getReturnType(), Fn, FI, args,
OMD->getLocation(), StartLoc);

if (OMD->isDirectMethod()) {
// This function is a direct call, it has to implement a nil check
// on entry.
//
// TODO: possibly have several entry points to elide the check
CGM.getObjCRuntime().GenerateDirectMethodPrologue(*this, Fn, OMD, CD);
}

// In ARC, certain methods get an extra cleanup.
if (CGM.getLangOpts().ObjCAutoRefCount &&
OMD->isInstanceMethod() &&
Expand Down
Loading