MadCoder created this revision.
MadCoder added reviewers: rjmccall, jfb, doug.gregor, dexonsmith.
Herald added subscribers: cfe-commits, kristof.beyls.
Herald added a project: clang.

__attribute__((objc_direct)) is an attribute on methods declaration and
implementation, categories, or extensions (for the latter two it applies
the attribute to all methods declared in the category/implementation),

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D69991

Files:
  clang/include/clang/AST/DeclObjC.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/DeclSpec.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/DeclObjC.cpp
  clang/lib/AST/DeclPrinter.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/CodeGen/CGObjC.cpp
  clang/lib/CodeGen/CGObjCGNU.cpp
  clang/lib/CodeGen/CGObjCMac.cpp
  clang/lib/CodeGen/CGObjCRuntime.h
  clang/lib/Parse/ParseObjc.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclObjC.cpp
  clang/lib/Sema/SemaExprObjC.cpp
  clang/lib/Sema/SemaObjCProperty.cpp
  clang/test/CodeGenObjC/direct-method.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaObjC/method-direct.m

Index: clang/test/SemaObjC/method-direct.m
===================================================================
--- /dev/null
+++ clang/test/SemaObjC/method-direct.m
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wselector-type-mismatch %s
+
+@protocol Proto
+- (void)protoMethod; // expected-note {{previous declaration is here}}
++ (void)classProtoMethod; // expected-note {{previous declaration is here}}
+@end
+
+@protocol ProtoDirectFail
+- (void)protoMethod __attribute__((objc_direct)); // expected-error {{objc_direct attribute cannot be applied to methods declared in an Objective-C protocol}}
++ (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{objc_direct attribute cannot be applied to methods declared in an Objective-C protocol}}
+@end
+
+__attribute__((objc_root_class))
+@interface Root
+- (void)rootRegular; // expected-note {{previous declaration is here}}
++ (void)classRootRegular; // expected-note {{previous declaration is here}}
+- (void)rootDirect __attribute__((objc_direct)); // expected-note {{previous declaration is here}};
++ (void)classRootDirect __attribute__((objc_direct)); // expected-note {{previous declaration is here}};
+- (void)notDirectInIface; // expected-note {{previous declaration is here}}
++ (void)classNotDirectInIface; // expected-note {{previous declaration is here}}
+@end
+
+__attribute__((objc_direct))
+@interface Root ()
+- (void)rootExtensionDirect; // expected-note {{previous declaration is here}}
++ (void)classRootExtensionDirect; // expected-note {{previous declaration is here}}
+@end
+
+__attribute__((objc_direct))
+@interface Root (Direct)
+- (void)rootCategoryDirect; // expected-note {{previous declaration is here}}
++ (void)classRootCategoryDirect; // expected-note {{previous declaration is here}}
+@end
+
+@interface Root ()
+- (void)rootExtensionRegular; // expected-note {{previous declaration is here}}
++ (void)classRootExtensionRegular; // expected-note {{previous declaration is here}}
+- (void)rootExtensionDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}}
++ (void)classRootExtensionDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}}
+@end
+
+@interface Root (Direct2)
+- (void)rootCategoryRegular; // expected-note {{previous declaration is here}}
++ (void)classRootCategoryRegular; // expected-note {{previous declaration is here}}
+- (void)rootCategoryDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}}
++ (void)classRootCategoryDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}}
+@end
+
+__attribute__((objc_root_class, objc_direct)) // expected-error {{'objc_direct' attribute only applies to Objective-C methods and Objective-C containers}}
+@interface SubDirectFail : Root
+- (instancetype)init;
+@end
+
+@interface Sub : Root <Proto>
+/* invalid overrides with directs */
+- (void)rootRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
++ (void)classRootRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
+- (void)protoMethod __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
++ (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
+- (void)rootExtensionRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
++ (void)classRootExtensionRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
+- (void)rootCategoryRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
++ (void)classRootCategoryRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}}
+
+/* invalid overrides of directs */
+- (void)rootDirect; // expected-error {{cannot override direct methods}}
++ (void)classRootDirect; // expected-error {{cannot override direct methods}}
+- (void)rootExtensionDirect; // expected-error {{cannot override direct methods}}
++ (void)classRootExtensionDirect; // expected-error {{cannot override direct methods}}
+- (void)rootExtensionDirect2; // expected-error {{cannot override direct methods}}
++ (void)classRootExtensionDirect2; // expected-error {{cannot override direct methods}}
+- (void)rootCategoryDirect; // expected-error {{cannot override direct methods}}
++ (void)classRootCategoryDirect; // expected-error {{cannot override direct methods}}
+- (void)rootCategoryDirect2; // expected-error {{cannot override direct methods}}
++ (void)classRootCategoryDirect2; // expected-error {{cannot override direct methods}}
+@end
+
+__attribute__((objc_direct_members))
+@implementation Root
+- (void)rootRegular { }
++ (void)classRootRegular { }
+- (void)rootDirect { }
++ (void)classRootDirect { }
+- (void)rootExtensionDirect { }
++ (void)classRootExtensionDirect { }
+- (void)rootExtensionRegular { }
++ (void)classRootExtensionRegular { }
+- (void)rootExtensionDirect2 { }
++ (void)classRootExtensionDirect2 { }
+- (void)notDirectInIface __attribute__((objc_direct)) // expected-error {{method implementation is direct but its declaration was not}}
+{
+}
++ (void)classNotDirectInIface __attribute__((objc_direct)) // expected-error {{method implementation is direct but its declaration was not}}
+{
+}
+- (void)direct1 { } // expected-note {{direct method 'direct1' declared here}}
+- (void)direct2 { } // expected-note {{direct method 'direct2' declared here}}
+@end
+
+@interface Foo : Root
+- (id)directMismatch1; // expected-note {{using}}
+- (id)directMismatch2; // expected-note {{method 'directMismatch2' declared here}}
+@end
+
+@interface Bar : Root
+- (void)directMismatch1 __attribute__((objc_direct)); // expected-note {{also found}}
+- (void)directMismatch2 __attribute__((objc_direct)); // expected-note {{method 'directMismatch2' declared here}}
+@end
+
+extern void callMethod(id obj);
+extern void useSel(SEL sel);
+
+void callMethod(id obj)
+{
+  [obj direct1]; // expected-error {{messaging unqualified id with a method that is possibly direct}}
+  [(Root *)obj direct1];
+  [obj directMismatch1]; // expected-warning {{multiple methods named 'directMismatch1' found}}
+  useSel(@selector(direct2)); // expected-error {{@selector expression formed with direct selector 'direct2'}}
+  useSel(@selector(directMismatch2)); // expected-warning {{several methods with selector 'directMismatch2' of mismatched types are found for the @selector expression}}
+}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -104,6 +104,8 @@
 // CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record)
 // CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method)
+// CHECK-NEXT: ObjCDirect (SubjectMatchRule_objc_method, SubjectMatchRule_objc_category)
+// CHECK-NEXT: ObjCDirectMembers (SubjectMatchRule_objc_implementation)
 // CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: ObjCExplicitProtocolImpl (SubjectMatchRule_objc_protocol)
 // CHECK-NEXT: ObjCExternallyRetained (SubjectMatchRule_variable_not_is_parameter, SubjectMatchRule_function, SubjectMatchRule_block, SubjectMatchRule_objc_method)
Index: clang/test/CodeGenObjC/direct-method.m
===================================================================
--- /dev/null
+++ clang/test/CodeGenObjC/direct-method.m
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o - | FileCheck %s
+
+struct my_complex_struct {
+	int a, b;
+};
+
+struct my_aggregate_struct {
+	int a, b;
+	char buf[128];
+};
+
+__attribute__((objc_root_class))
+@interface Root
+@end
+
+@implementation Root
+- (int)getInt __attribute__((objc_direct))
+{
+	return 42;
+}
+- (struct my_complex_struct)getComplex __attribute__((objc_direct))
+{
+	struct my_complex_struct st = { .a = 42 };
+	return st;
+}
+- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct))
+{
+	struct my_aggregate_struct st = { .a = 42 };
+	return st;
+}
+@end
+
+// CHECK-LABEL: define hidden i32 @"\01-[Root getInt]"(
+// CHECK-LABEL: define hidden i64 @"\01-[Root getComplex]"(
+// CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"(
Index: clang/lib/Sema/SemaObjCProperty.cpp
===================================================================
--- clang/lib/Sema/SemaObjCProperty.cpp
+++ clang/lib/Sema/SemaObjCProperty.cpp
@@ -306,6 +306,8 @@
     attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_atomic;
   if (Attributes & ObjCDeclSpec::DQ_PR_class)
     attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_class;
+  if (Attributes & ObjCDeclSpec::DQ_PR_direct)
+    attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_direct;
 
   return (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten;
 }
@@ -705,9 +707,12 @@
   if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable)
     PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable);
 
- if (Attributes & ObjCDeclSpec::DQ_PR_class)
+  if (Attributes & ObjCDeclSpec::DQ_PR_class)
     PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_class);
 
+  if (Attributes & ObjCDeclSpec::DQ_PR_direct)
+    PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_direct);
+
   return PDecl;
 }
 
@@ -2410,6 +2415,9 @@
 
     AddPropertyAttrs(*this, GetterMethod, property);
 
+    if (property->isDirectProperty())
+      GetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc));
+
     if (property->hasAttr<NSReturnsNotRetainedAttr>())
       GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context,
                                                                      Loc));
@@ -2483,6 +2491,9 @@
 
       AddPropertyAttrs(*this, SetterMethod, property);
 
+      if (property->isDirectProperty())
+        SetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc));
+
       CD->addDecl(SetterMethod);
       if (const SectionAttr *SA = property->getAttr<SectionAttr>())
         SetterMethod->addAttr(SectionAttr::CreateImplicit(
Index: clang/lib/Sema/SemaExprObjC.cpp
===================================================================
--- clang/lib/Sema/SemaExprObjC.cpp
+++ clang/lib/Sema/SemaExprObjC.cpp
@@ -1118,6 +1118,17 @@
   return BuildObjCEncodeExpression(AtLoc, TInfo, RParenLoc);
 }
 
+static void HelperNoteMethodDeclaredAt(Sema &S, ObjCMethodDecl *Method)
+{
+  if (Method->isDirectMethod()) {
+    S.Diag(Method->getLocation(), diag::note_direct_method_declared_at)
+      << Method->getDeclName();
+  } else {
+    S.Diag(Method->getLocation(), diag::note_method_declared_at)
+      << Method->getDeclName();
+  }
+}
+
 static bool HelperToDiagnoseMismatchedMethodsInGlobalPool(Sema &S,
                                                SourceLocation AtLoc,
                                                SourceLocation LParenLoc,
@@ -1139,11 +1150,9 @@
         S.Diag(AtLoc, diag::warn_multiple_selectors)
           << Method->getSelector() << FixItHint::CreateInsertion(LParenLoc, "(")
           << FixItHint::CreateInsertion(RParenLoc, ")");
-        S.Diag(Method->getLocation(), diag::note_method_declared_at)
-          << Method->getDeclName();
+        HelperNoteMethodDeclaredAt(S, Method);
       }
-      S.Diag(MatchingMethodDecl->getLocation(), diag::note_method_declared_at)
-        << MatchingMethodDecl->getDeclName();
+      HelperNoteMethodDeclaredAt(S, MatchingMethodDecl);
     }
   }
   return Warned;
@@ -1174,6 +1183,35 @@
   }
 }
 
+static void HelperToDiagnoseDirectSelectorsExpr(Sema &S,
+                                                SourceLocation AtLoc,
+                                                Selector Sel,
+                                                ObjCMethodList &MethList,
+                                                bool &onlyDirect) {
+  ObjCMethodList *M = &MethList;
+  for (M = M->getNext(); M; M=M->getNext()) {
+    ObjCMethodDecl *Method = M->getMethod();
+    if (Method->getSelector() != Sel)
+      continue;
+    if (!Method->isDirectMethod())
+      onlyDirect = false;
+  }
+}
+
+static void DiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc,
+                                        Selector Sel, bool &onlyDirect) {
+  for (Sema::GlobalMethodPool::iterator b = S.MethodPool.begin(),
+       e = S.MethodPool.end(); b != e; b++) {
+    // first, instance methods
+    ObjCMethodList &InstMethList = b->second.first;
+    HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, InstMethList, onlyDirect);
+
+    // second, class methods
+    ObjCMethodList &ClsMethList = b->second.second;
+    HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, ClsMethList, onlyDirect);
+  }
+}
+
 ExprResult Sema::ParseObjCSelectorExpression(Selector Sel,
                                              SourceLocation AtLoc,
                                              SourceLocation SelLoc,
@@ -1196,9 +1234,18 @@
 
     } else
         Diag(SelLoc, diag::warn_undeclared_selector) << Sel;
-  } else
+  } else {
+    bool onlyDirect = Method->isDirectMethod();
+    DiagnoseDirectSelectorsExpr(*this, AtLoc, Sel, onlyDirect);
     DiagnoseMismatchedSelectors(*this, AtLoc, Method, LParenLoc, RParenLoc,
                                 WarnMultipleSelectors);
+    if (onlyDirect) {
+      Diag(AtLoc, diag::err_direct_selector_expression)
+        << Method->getSelector();
+      Diag(Method->getLocation(), diag::note_direct_method_declared_at)
+        << Method->getDeclName();
+    }
+  }
 
   if (Method &&
       Method->getImplementationControl() != ObjCMethodDecl::Optional &&
@@ -2771,9 +2818,6 @@
     }
   }
 
-  if (ReceiverType->isObjCIdType() && !isImplicit)
-    Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id);
-
   // There's a somewhat weird interaction here where we assume that we
   // won't actually have a method unless we also don't need to do some
   // of the more detailed type-checking on the receiver.
@@ -2975,6 +3019,17 @@
     (Method && Method->getMethodFamily() == OMF_init)
       ? getEnclosingFunction() : nullptr;
 
+  if (ReceiverType->isObjCIdType() && !isImplicit) {
+    if (Method && Method->isDirectMethod()) {
+      Diag(Receiver->getExprLoc(),
+           diag::err_messaging_unqualified_id_with_direct_method);
+      Diag(Method->getLocation(), diag::note_direct_method_declared_at)
+        << Method->getDeclName();
+    } else {
+      Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id);
+    }
+  }
+
   if (DIFunctionScopeInfo &&
       DIFunctionScopeInfo->ObjCIsDesignatedInit &&
       (SuperLoc.isValid() || isSelfExpr(Receiver))) {
Index: clang/lib/Sema/SemaDeclObjC.cpp
===================================================================
--- clang/lib/Sema/SemaDeclObjC.cpp
+++ clang/lib/Sema/SemaDeclObjC.cpp
@@ -3233,6 +3233,9 @@
   if (left->isHidden() || right->isHidden())
     return false;
 
+  if (left->isDirectMethod() != right->isDirectMethod())
+    return false;
+
   if (getLangOpts().ObjCAutoRefCount &&
       (left->hasAttr<NSReturnsRetainedAttr>()
          != right->hasAttr<NSReturnsRetainedAttr>() ||
@@ -3424,6 +3427,9 @@
   if (!chosen->isInstanceMethod())
     return false;
 
+  if (chosen->isDirectMethod() != other->isDirectMethod())
+    return false;
+
   Selector sel = chosen->getSelector();
   if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length")
     return false;
@@ -4314,6 +4320,19 @@
 };
 } // end anonymous namespace
 
+
+void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
+                                          ObjCMethodDecl *overridden)
+{
+  if (const auto *attr = overridden->getAttr<ObjCDirectAttr>()) {
+    Diag(method->getLocation(), diag::err_objc_override_direct_method);
+    Diag(attr->getLocation(), diag::note_previous_declaration);
+  } else if (const auto *attr = method->getAttr<ObjCDirectAttr>()) {
+    Diag(attr->getLocation(), diag::err_objc_direct_on_override);
+    Diag(overridden->getLocation(), diag::note_previous_declaration);
+  }
+}
+
 void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
                                     ObjCInterfaceDecl *CurrentClass,
                                     ResultTypeCompatibilityKind RTC) {
@@ -4332,8 +4351,8 @@
       if (isa<ObjCProtocolDecl>(overridden->getDeclContext()) ||
           CurrentClass != overridden->getClassInterface() ||
           overridden->isOverriding()) {
+        CheckObjCMethodDirectOverrides(ObjCMethod, overridden);
         hasOverriddenMethodsInBaseOrProtocol = true;
-
       } else if (isa<ObjCImplDecl>(ObjCMethod->getDeclContext())) {
         // OverrideSearch will return as "overridden" the same method in the
         // interface. For hasOverriddenMethodsInBaseOrProtocol, we need to
@@ -4357,6 +4376,7 @@
               for (ObjCMethodDecl *SuperOverridden : overrides) {
                 if (isa<ObjCProtocolDecl>(SuperOverridden->getDeclContext()) ||
                     CurrentClass != SuperOverridden->getClassInterface()) {
+                  CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden);
                   hasOverriddenMethodsInBaseOrProtocol = true;
                   overridden->setOverriding(true);
                   break;
@@ -4464,6 +4484,12 @@
                                             method->getLocation()));
   }
 
+  if (!method->isDirectMethod())
+    if (const auto *attr = prevMethod->getAttr<ObjCDirectAttr>()) {
+      method->addAttr(
+        ObjCDirectAttr::CreateImplicit(S.Context, attr->getLocation()));
+    }
+
   // Merge nullability of the result type.
   QualType newReturnType
     = mergeTypeNullabilityForRedecl(
@@ -4672,6 +4698,12 @@
       if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
                                           ObjCMethod->isInstanceMethod())) {
         mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD);
+        if (const auto *attr = ObjCMethod->getAttr<ObjCDirectAttr>()) {
+          if (!IMD->isDirectMethod()) {
+            Diag(attr->getLocation(), diag::err_objc_direct_missing_on_decl);
+            Diag(IMD->getLocation(), diag::note_previous_declaration);
+          }
+        }
 
         // Warn about defining -dealloc in a category.
         if (isa<ObjCCategoryImplDecl>(ImpDecl) && IMD->isOverriding() &&
@@ -4679,6 +4711,9 @@
           Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
             << ObjCMethod->getDeclName();
         }
+      } else if (ImpDecl->hasAttr<ObjCDirectMembersAttr>()) {
+        ObjCMethod->addAttr(
+          ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
       }
 
       // Warn if a method declared in a protocol to which a category or
@@ -4698,6 +4733,10 @@
           }
     }
   } else {
+    if (!ObjCMethod->isDirectMethod() && ClassDecl->hasAttr<ObjCDirectAttr>()) {
+      ObjCMethod->addAttr(
+        ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
+    }
     cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);
   }
 
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -2600,6 +2600,16 @@
     D->addAttr(newAttr);
 }
 
+static void handleObjCDirectAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  // objc_direct cannot be set on methods declared in the context of a protocol
+  if (isa<ObjCProtocolDecl>(D->getDeclContext())) {
+    S.Diag(AL.getLoc(), diag::err_objc_direct_on_protocol);
+    return;
+  }
+
+  handleSimpleAttribute<ObjCDirectAttr>(S, D, AL);
+}
+
 static void handleObjCMethodFamilyAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   const auto *M = cast<ObjCMethodDecl>(D);
   if (!AL.isArgIdent(0)) {
@@ -6909,6 +6919,12 @@
   case ParsedAttr::AT_ObjCRootClass:
     handleSimpleAttribute<ObjCRootClassAttr>(S, D, AL);
     break;
+  case ParsedAttr::AT_ObjCDirect:
+    handleObjCDirectAttr(S, D, AL);
+    break;
+  case ParsedAttr::AT_ObjCDirectMembers:
+    handleSimpleAttribute<ObjCDirectMembersAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_ObjCNonLazyClass:
     handleSimpleAttribute<ObjCNonLazyClassAttr>(S, D, AL);
     break;
Index: clang/lib/Parse/ParseObjc.cpp
===================================================================
--- clang/lib/Parse/ParseObjc.cpp
+++ clang/lib/Parse/ParseObjc.cpp
@@ -822,6 +822,7 @@
 ///   property-attribute:
 ///     getter '=' identifier
 ///     setter '=' identifier ':'
+///     direct
 ///     readonly
 ///     readwrite
 ///     assign
@@ -954,6 +955,8 @@
       DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable);
     } else if (II->isStr("class")) {
       DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class);
+    } else if (II->isStr("direct")) {
+      DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_direct);
     } else {
       Diag(AttrName, diag::err_objc_expected_property_attr) << II;
       SkipUntil(tok::r_paren, StopAtSemi);
Index: clang/lib/CodeGen/CGObjCRuntime.h
===================================================================
--- clang/lib/CodeGen/CGObjCRuntime.h
+++ clang/lib/CodeGen/CGObjCRuntime.h
@@ -169,6 +169,27 @@
                       const ObjCInterfaceDecl *Class = nullptr,
                       const ObjCMethodDecl *Method = nullptr) = 0;
 
+  /// The ObjC runtime may provide entrypoints that are likely to be faster
+  /// than an ordinary message send of the appropriate selector.
+  ///
+  /// The entrypoints are guaranteed to be equivalent to just sending the
+  /// corresponding message.  If the entrypoint is implemented naively as just a
+  /// message send, using it is a trade-off: it sacrifices a few cycles of
+  /// overhead to save a small amount of code.  However, it's possible for
+  /// runtimes to detect and special-case classes that use "standard"
+  /// behavior; if that's dynamically a large proportion of all objects, using
+  /// the entrypoint will also be faster than using a message send.
+  ///
+  /// If the runtime does support a required entrypoint, then this method will
+  /// generate a call and return the resulting value.  Otherwise it will return
+  /// None and the caller can generate a msgSend instead.
+  Optional<llvm::Value *>
+  tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
+                                    llvm::Value *Receiver,
+                                    const CallArgList& Args, Selector Sel,
+                                    const ObjCMethodDecl *method,
+                                    bool isClassMessage);
+
   /// Generate an Objective-C message send operation to the super
   /// class initiated in a method for Class and with the given Self
   /// object.
@@ -205,6 +226,12 @@
   virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD,
                                          const ObjCContainerDecl *CD) = 0;
 
+  /// Generates prologue for direct Objective-C Methods.
+  virtual void GenerateDirectMethodPrologue(CodeGenFunction &CGF,
+                                            llvm::Function *Fn,
+                                            const ObjCMethodDecl *OMD,
+                                            const ObjCContainerDecl *CD) = 0;
+
   /// Return the runtime function for getting properties.
   virtual llvm::FunctionCallee GetPropertyGetFunction() = 0;
 
Index: clang/lib/CodeGen/CGObjCMac.cpp
===================================================================
--- clang/lib/CodeGen/CGObjCMac.cpp
+++ clang/lib/CodeGen/CGObjCMac.cpp
@@ -874,6 +874,10 @@
   /// this translation unit.
   llvm::DenseMap<const ObjCMethodDecl*, llvm::Function*> MethodDefinitions;
 
+  /// DirectMethodDefinitions - map of direct methods which have been defined in
+  /// this translation unit.
+  llvm::DenseMap<const ObjCMethodDecl*, llvm::Function*> DirectMethodDefinitions;
+
   /// PropertyNames - uniqued method variable names.
   llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> PropertyNames;
 
@@ -1065,7 +1069,7 @@
   CodeGen::RValue EmitMessageSend(CodeGen::CodeGenFunction &CGF,
                                   ReturnValueSlot Return,
                                   QualType ResultType,
-                                  llvm::Value *Sel,
+                                  Selector Sel,
                                   llvm::Value *Arg0,
                                   QualType Arg0Ty,
                                   bool IsSuper,
@@ -1092,6 +1096,14 @@
   llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD,
                                  const ObjCContainerDecl *CD=nullptr) override;
 
+  llvm::Function *GenerateDirectMethod(const ObjCMethodDecl *OMD,
+                                       const ObjCContainerDecl *CD);
+
+  void GenerateDirectMethodPrologue(CodeGenFunction &CGF,
+                                    llvm::Function *Fn,
+                                    const ObjCMethodDecl *OMD,
+                                    const ObjCContainerDecl *CD) override;
+
   void GenerateProtocol(const ObjCProtocolDecl *PD) override;
 
   /// GetOrEmitProtocol - Get the protocol object for the given
@@ -1573,9 +1585,13 @@
     // base of the ivar access is a parameter to an Objective C method.
     // However, because the parameters are not available in the current
     // interface, we cannot perform this check.
+	//
+	// Note that for direct methods, because objc_msgSend is skipped,
+	// and that the method may be inlined, this optimization actually
+	// can't be performed.
     if (const ObjCMethodDecl *MD =
           dyn_cast_or_null<ObjCMethodDecl>(CGF.CurFuncDecl))
-      if (MD->isInstanceMethod())
+      if (MD->isInstanceMethod() && !MD->isDirectMethod())
         if (const ObjCInterfaceDecl *ID = MD->getClassInterface())
           return IV->getContainingInterface()->isSuperClassOf(ID);
     return false;
@@ -2103,8 +2119,7 @@
     CGM.getTypes().ConvertType(CGF.getContext().getObjCClassType());
   Target = CGF.Builder.CreateBitCast(Target, ClassTy);
   CGF.Builder.CreateStore(Target, CGF.Builder.CreateStructGEP(ObjCSuper, 1));
-  return EmitMessageSend(CGF, Return, ResultType,
-                         EmitSelector(CGF, Sel),
+  return EmitMessageSend(CGF, Return, ResultType, Sel,
                          ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
                          true, CallArgs, Method, Class, ObjCTypes);
 }
@@ -2118,8 +2133,7 @@
                                                const CallArgList &CallArgs,
                                                const ObjCInterfaceDecl *Class,
                                                const ObjCMethodDecl *Method) {
-  return EmitMessageSend(CGF, Return, ResultType,
-                         EmitSelector(CGF, Sel),
+  return EmitMessageSend(CGF, Return, ResultType, Sel,
                          Receiver, CGF.getContext().getObjCIdType(),
                          false, CallArgs, Method, Class, ObjCTypes);
 }
@@ -2137,7 +2151,7 @@
 CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
                                  ReturnValueSlot Return,
                                  QualType ResultType,
-                                 llvm::Value *Sel,
+                                 Selector Sel,
                                  llvm::Value *Arg0,
                                  QualType Arg0Ty,
                                  bool IsSuper,
@@ -2145,11 +2159,22 @@
                                  const ObjCMethodDecl *Method,
                                  const ObjCInterfaceDecl *ClassReceiver,
                                  const ObjCCommonTypesHelper &ObjCTypes) {
+  CodeGenTypes &Types = CGM.getTypes();
   CallArgList ActualArgs;
   if (!IsSuper)
     Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy);
   ActualArgs.add(RValue::get(Arg0), Arg0Ty);
-  ActualArgs.add(RValue::get(Sel), CGF.getContext().getObjCSelType());
+  if (Method && Method->isDirectMethod()) {
+    // direct methods will synthesize the proper _cmd so just don't bother
+    // with the argument
+    assert(!IsSuper);
+    auto selTy = CGF.getContext().getObjCSelType();
+    auto undefSel = llvm::UndefValue::get(Types.ConvertType(selTy));
+    ActualArgs.add(RValue::get(undefSel), selTy);
+  } else {
+    ActualArgs.add(RValue::get(GetSelector(CGF, Sel)),
+                   CGF.getContext().getObjCSelType());
+  }
   ActualArgs.addFrom(CallArgs);
 
   // If we're calling a method, use the formal signature.
@@ -2190,7 +2215,9 @@
   bool RequiresNullCheck = false;
 
   llvm::FunctionCallee Fn = nullptr;
-  if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) {
+  if (Method && Method->isDirectMethod()) {
+    Fn = GenerateDirectMethod(Method, Method->getClassInterface());
+  } else if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) {
     if (ReceiverCanBeNull) RequiresNullCheck = true;
     Fn = (ObjCABI == 2) ?  ObjCTypes.getSendStretFn2(IsSuper)
       : ObjCTypes.getSendStretFn(IsSuper);
@@ -3297,6 +3324,8 @@
   values.addInt(ObjCTypes.IntTy, Properties.size());
   auto propertiesArray = values.beginArray(ObjCTypes.PropertyTy);
   for (auto PD : Properties) {
+    if (PD->isDirectProperty())
+      continue;
     auto property = propertiesArray.beginStruct(ObjCTypes.PropertyTy);
     property.add(GetPropertyName(PD->getIdentifier()));
     property.add(GetPropertyTypeString(PD, Container));
@@ -3372,7 +3401,8 @@
   };
   SmallVector<const ObjCMethodDecl *, 16> Methods[NumMethodLists];
   for (const auto *MD : OCD->methods()) {
-    Methods[unsigned(MD->isClassMethod())].push_back(MD);
+    if (!MD->isDirectMethod())
+      Methods[unsigned(MD->isClassMethod())].push_back(MD);
   }
 
   Values.add(GetClassName(OCD->getName()));
@@ -3554,19 +3584,22 @@
   };
   SmallVector<const ObjCMethodDecl *, 16> Methods[NumMethodLists];
   for (const auto *MD : ID->methods()) {
-    Methods[unsigned(MD->isClassMethod())].push_back(MD);
+    if (!MD->isDirectMethod())
+      Methods[unsigned(MD->isClassMethod())].push_back(MD);
   }
 
   for (const auto *PID : ID->property_impls()) {
     if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) {
       ObjCPropertyDecl *PD = PID->getPropertyDecl();
 
-      if (ObjCMethodDecl *MD = PD->getGetterMethodDecl())
-        if (GetMethodDefinition(MD))
-          Methods[InstanceMethods].push_back(MD);
-      if (ObjCMethodDecl *MD = PD->getSetterMethodDecl())
-        if (GetMethodDefinition(MD))
-          Methods[InstanceMethods].push_back(MD);
+      if (!PD->isDirectProperty()) {
+        if (ObjCMethodDecl *MD = PD->getGetterMethodDecl())
+          if (GetMethodDefinition(MD))
+            Methods[InstanceMethods].push_back(MD);
+        if (ObjCMethodDecl *MD = PD->getSetterMethodDecl())
+          if (GetMethodDefinition(MD))
+            Methods[InstanceMethods].push_back(MD);
+      }
     }
   }
 
@@ -3959,7 +3992,8 @@
   values.addInt(ObjCTypes.IntTy, methods.size());
   auto methodArray = values.beginArray(ObjCTypes.MethodTy);
   for (auto MD : methods) {
-    emitMethodConstant(methodArray, MD);
+    if (!MD->isDirectMethod())
+      emitMethodConstant(methodArray, MD);
   }
   methodArray.finishAndAddTo(values);
 
@@ -3970,22 +4004,136 @@
 
 llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD,
                                                 const ObjCContainerDecl *CD) {
+  llvm::Function *Method;
+
+  if (OMD->isDirectMethod()) {
+    Method = GenerateDirectMethod(OMD, CD);
+  } else {
+    SmallString<256> Name;
+    GetNameForMethod(OMD, CD, Name);
+
+    CodeGenTypes &Types = CGM.getTypes();
+    llvm::FunctionType *MethodTy =
+      Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD));
+    Method =
+      llvm::Function::Create(MethodTy,
+                             llvm::GlobalValue::InternalLinkage,
+                             Name.str(),
+                             &CGM.getModule());
+  }
+
+  MethodDefinitions.insert(std::make_pair(OMD, Method));
+
+  return Method;
+}
+
+llvm::Function *CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD,
+                                                      const ObjCContainerDecl *CD) {
+  auto I = DirectMethodDefinitions.find(OMD);
+  if (I != DirectMethodDefinitions.end())
+    return I->second;
+
   SmallString<256> Name;
-  GetNameForMethod(OMD, CD, Name);
+  GetNameForMethod(OMD, OMD->getClassInterface(), Name);
 
   CodeGenTypes &Types = CGM.getTypes();
   llvm::FunctionType *MethodTy =
     Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD));
   llvm::Function *Method =
     llvm::Function::Create(MethodTy,
-                           llvm::GlobalValue::InternalLinkage,
+                           llvm::GlobalValue::ExternalLinkage,
                            Name.str(),
                            &CGM.getModule());
-  MethodDefinitions.insert(std::make_pair(OMD, Method));
+  Method->setVisibility(llvm::Function::HiddenVisibility);
+  DirectMethodDefinitions.insert(std::make_pair(OMD, Method));
 
   return Method;
 }
 
+void CGObjCCommonMac::GenerateDirectMethodPrologue(CodeGenFunction &CGF,
+                                                   llvm::Function *Fn,
+                                                   const ObjCMethodDecl *OMD,
+                                                   const ObjCContainerDecl *CD)
+{
+  auto &Builder = CGF.Builder;
+  bool ReceiverCanBeNull = true;
+  auto selfValue = &*Fn->arg_begin();
+
+  // Generate:
+  //
+  // /* for class methods only to force class lazy initialization */
+  // self = [self self];
+  //
+  // /* unless the receiver is never NULL */
+  // if (self == nil) {
+  //     return (ReturnType){ };
+  // }
+  //
+  // _cmd = @selector(...)
+  // ...
+
+  if (OMD->isClassMethod()) {
+    const ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(CD);
+    Selector SelfSel = GetNullarySelector("self", CGM.getContext());
+    auto ResultType = CGF.getContext().getObjCIdType();
+    RValue result;
+    CallArgList Args;
+
+    // attempt hard to generate objc_opt_self() which is as cheap as it can be
+    // to realize a class lazily
+    //
+    // TODO: make sure that when the inliner kicks in we do this once only
+    if (Optional<llvm::Value *> SpecializedResult =
+        tryGenerateSpecializedMessageSend(CGF, ResultType, selfValue, Args,
+                                          SelfSel, nullptr, true)) {
+      result = RValue::get(SpecializedResult.getValue());
+    } else {
+      result = GenerateMessageSend(CGF, ReturnValueSlot(), ResultType,
+                                   SelfSel, selfValue, Args, OID, nullptr);
+    }
+    Builder.CreateStore(result.getScalarVal(),
+                        CGF.GetAddrOfLocalVar(OMD->getSelfDecl()));
+
+    ReceiverCanBeNull = !OID || isWeakLinkedClass(OID);
+  }
+
+  if (ReceiverCanBeNull) {
+    llvm::BasicBlock *SelfIsNilBlock = CGF.createBasicBlock("objc_direct_method.self_is_nil");
+    llvm::BasicBlock *ContBlock = CGF.createBasicBlock("objc_direct_method.cont");
+
+    // if (self == nil) {
+    auto selfTy = cast<llvm::PointerType>(selfValue->getType());
+    auto Zero = llvm::ConstantPointerNull::get(selfTy);
+
+    llvm::MDBuilder MDHelper(CGM.getLLVMContext());
+    Builder.CreateCondBr(Builder.CreateICmpEQ(selfValue, Zero), SelfIsNilBlock,
+                         ContBlock, MDHelper.createBranchWeights(1, 1<<20));
+
+    CGF.EmitBlock(SelfIsNilBlock);
+
+    //   return (ReturnType){ };
+    auto retTy = OMD->getReturnType();
+    Builder.SetInsertPoint(SelfIsNilBlock);
+    if (retTy->isVoidType()) {
+      Builder.CreateRetVoid();
+    } else {
+      CGF.EmitNullInitialization(CGF.ReturnValue, retTy);
+      CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
+    }
+    // }
+
+    // rest of the body
+    CGF.EmitBlock(ContBlock);
+    Builder.SetInsertPoint(ContBlock);
+  }
+
+  // only synthesize _cmd if it's referenced
+  if (OMD->getCmdDecl()->isUsed()) {
+    Builder.CreateStore(GetSelector(CGF, OMD),
+                        CGF.GetAddrOfLocalVar(OMD->getCmdDecl()));
+  }
+}
+
 llvm::GlobalVariable *CGObjCCommonMac::CreateMetadataVar(Twine Name,
                                                ConstantStructBuilder &Init,
                                                          StringRef Section,
@@ -6228,21 +6376,24 @@
   SmallVector<const ObjCMethodDecl*, 16> methods;
   if (flags & NonFragileABI_Class_Meta) {
     for (const auto *MD : ID->class_methods())
-      methods.push_back(MD);
+      if (!MD->isDirectMethod())
+        methods.push_back(MD);
   } else {
     for (const auto *MD : ID->instance_methods())
-      methods.push_back(MD);
+      if (!MD->isDirectMethod())
+        methods.push_back(MD);
 
     for (const auto *PID : ID->property_impls()) {
       if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize){
         ObjCPropertyDecl *PD = PID->getPropertyDecl();
-
-        if (auto MD = PD->getGetterMethodDecl())
-          if (GetMethodDefinition(MD))
-            methods.push_back(MD);
-        if (auto MD = PD->getSetterMethodDecl())
-          if (GetMethodDefinition(MD))
-            methods.push_back(MD);
+        if (!PD->isDirectProperty()) {
+          if (auto MD = PD->getGetterMethodDecl())
+            if (GetMethodDefinition(MD))
+              methods.push_back(MD);
+          if (auto MD = PD->getSetterMethodDecl())
+            if (GetMethodDefinition(MD))
+              methods.push_back(MD);
+        }
       }
     }
   }
@@ -6565,10 +6716,12 @@
   SmallVector<const ObjCMethodDecl *, 16> instanceMethods;
   SmallVector<const ObjCMethodDecl *, 8> classMethods;
   for (const auto *MD : OCD->methods()) {
-    if (MD->isInstanceMethod()) {
-      instanceMethods.push_back(MD);
-    } else {
-      classMethods.push_back(MD);
+    if (!MD->isDirectMethod()) {
+      if (MD->isInstanceMethod()) {
+        instanceMethods.push_back(MD);
+      } else {
+        classMethods.push_back(MD);
+      }
     }
   }
 
@@ -7234,8 +7387,7 @@
     ? EmitVTableMessageSend(CGF, Return, ResultType, Sel,
                             Receiver, CGF.getContext().getObjCIdType(),
                             false, CallArgs, Method)
-    : EmitMessageSend(CGF, Return, ResultType,
-                      EmitSelector(CGF, Sel),
+    : EmitMessageSend(CGF, Return, ResultType, Sel,
                       Receiver, CGF.getContext().getObjCIdType(),
                       false, CallArgs, Method, Class, ObjCTypes);
 }
@@ -7466,8 +7618,7 @@
     ? EmitVTableMessageSend(CGF, Return, ResultType, Sel,
                             ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
                             true, CallArgs, Method)
-    : EmitMessageSend(CGF, Return, ResultType,
-                      EmitSelector(CGF, Sel),
+    : EmitMessageSend(CGF, Return, ResultType, Sel,
                       ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
                       true, CallArgs, Method, Class, ObjCTypes);
 }
@@ -7492,7 +7643,7 @@
       llvm::ConstantExpr::getBitCast(GetMethodVarName(Sel),
                                      ObjCTypes.SelectorPtrTy);
     std::string SectionName =
-        GetSectionName("__objc_selrefs", "literal_pointers,no_dead_strip");
+        GetSectionName("__objc_selrefs", "literal_pointers");
     Entry = new llvm::GlobalVariable(
         CGM.getModule(), ObjCTypes.SelectorPtrTy, false,
         getLinkageTypeForObjCMetadata(CGM, SectionName), Casted,
Index: clang/lib/CodeGen/CGObjCGNU.cpp
===================================================================
--- clang/lib/CodeGen/CGObjCGNU.cpp
+++ clang/lib/CodeGen/CGObjCGNU.cpp
@@ -606,6 +606,10 @@
 
   llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD,
                                  const ObjCContainerDecl *CD) override;
+  void GenerateDirectMethodPrologue(CodeGenFunction &CGF,
+                                    llvm::Function *Fn,
+                                    const ObjCMethodDecl *OMD,
+                                    const ObjCContainerDecl *CD) override;
   void GenerateCategory(const ObjCCategoryImplDecl *CMD) override;
   void GenerateClass(const ObjCImplementationDecl *ClassDecl) override;
   void RegisterAlias(const ObjCCompatibleAliasDecl *OAD) override;
@@ -3873,6 +3877,14 @@
   return Method;
 }
 
+void CGObjCGNU::GenerateDirectMethodPrologue(CodeGenFunction &CGF,
+                                             llvm::Function *Fn,
+                                             const ObjCMethodDecl *OMD,
+                                             const ObjCContainerDecl *CD)
+{
+  // GNU runtime doesn't support direct calls at this time
+}
+
 llvm::FunctionCallee CGObjCGNU::GetPropertyGetFunction() {
   return GetPropertyFn;
 }
Index: clang/lib/CodeGen/CGObjC.cpp
===================================================================
--- clang/lib/CodeGen/CGObjC.cpp
+++ clang/lib/CodeGen/CGObjC.cpp
@@ -353,26 +353,13 @@
   return nullptr;
 }
 
-/// The ObjC runtime may provide entrypoints that are likely to be faster
-/// than an ordinary message send of the appropriate selector.
-///
-/// The entrypoints are guaranteed to be equivalent to just sending the
-/// corresponding message.  If the entrypoint is implemented naively as just a
-/// message send, using it is a trade-off: it sacrifices a few cycles of
-/// overhead to save a small amount of code.  However, it's possible for
-/// runtimes to detect and special-case classes that use "standard"
-/// behavior; if that's dynamically a large proportion of all objects, using
-/// the entrypoint will also be faster than using a message send.
-///
-/// If the runtime does support a required entrypoint, then this method will
-/// generate a call and return the resulting value.  Otherwise it will return
-/// None and the caller can generate a msgSend instead.
-static Optional<llvm::Value *>
-tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
-                                  llvm::Value *Receiver,
-                                  const CallArgList& Args, Selector Sel,
-                                  const ObjCMethodDecl *method,
-                                  bool isClassMessage) {
+Optional<llvm::Value *>
+CGObjCRuntime::tryGenerateSpecializedMessageSend(CodeGenFunction &CGF,
+                                                 QualType ResultType,
+                                                 llvm::Value *Receiver,
+                                                 const CallArgList& Args, Selector Sel,
+                                                 const ObjCMethodDecl *method,
+                                                 bool isClassMessage) {
   auto &CGM = CGF.CGM;
   if (!CGM.getCodeGenOpts().ObjCConvertMessagesToRuntimeCalls)
     return None;
@@ -612,9 +599,10 @@
   } else {
     // Call runtime methods directly if we can.
     if (Optional<llvm::Value *> SpecializedResult =
-            tryGenerateSpecializedMessageSend(*this, ResultType, Receiver, Args,
-                                              E->getSelector(), method,
-                                              isClassMessage)) {
+            Runtime.tryGenerateSpecializedMessageSend(*this, ResultType,
+                                                      Receiver, Args,
+                                                      E->getSelector(), method,
+                                                      isClassMessage)) {
       result = RValue::get(SpecializedResult.getValue());
     } else {
       result = Runtime.GenerateMessageSend(*this, Return, ResultType,
@@ -696,6 +684,12 @@
   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.
+    CGM.getObjCRuntime().GenerateDirectMethodPrologue(*this, Fn, OMD, CD);
+  }
+
   // In ARC, certain methods get an extra cleanup.
   if (CGM.getLangOpts().ObjCAutoRefCount &&
       OMD->isInstanceMethod() &&
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -1921,6 +1921,8 @@
       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)
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -1013,6 +1013,7 @@
     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",
Index: clang/lib/AST/DeclPrinter.cpp
===================================================================
--- clang/lib/AST/DeclPrinter.cpp
+++ clang/lib/AST/DeclPrinter.cpp
@@ -1469,6 +1469,11 @@
       first = false;
     }
 
+    if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_direct) {
+      Out << (first ? "" : ", ") << "direct";
+      first = false;
+    }
+
     if (PDecl->getPropertyAttributes() &
         ObjCPropertyDecl::OBJC_PR_nonatomic) {
       Out << (first ? "" : ", ") << "nonatomic";
Index: clang/lib/AST/DeclObjC.cpp
===================================================================
--- clang/lib/AST/DeclObjC.cpp
+++ clang/lib/AST/DeclObjC.cpp
@@ -823,6 +823,10 @@
                                     Selector(), QualType(), nullptr, nullptr);
 }
 
+bool ObjCMethodDecl::isDirectMethod() const {
+  return hasAttr<ObjCDirectAttr>();
+}
+
 bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
   return getMethodFamily() == OMF_init &&
       hasAttr<ObjCDesignatedInitializerAttr>();
@@ -1077,7 +1081,7 @@
 QualType ObjCMethodDecl::getSelfType(ASTContext &Context,
                                      const ObjCInterfaceDecl *OID,
                                      bool &selfIsPseudoStrong,
-                                     bool &selfIsConsumed) {
+                                     bool &selfIsConsumed) const {
   QualType selfTy;
   selfIsPseudoStrong = false;
   selfIsConsumed = false;
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -8901,6 +8901,9 @@
     RTC_Unknown
   };
 
+  void CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
+                                      ObjCMethodDecl *overridden);
+
   void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
                                 ObjCInterfaceDecl *CurrentClass,
                                 ResultTypeCompatibilityKind RTC);
Index: clang/include/clang/Sema/DeclSpec.h
===================================================================
--- clang/include/clang/Sema/DeclSpec.h
+++ clang/include/clang/Sema/DeclSpec.h
@@ -833,7 +833,8 @@
     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()
@@ -903,7 +904,7 @@
   unsigned objcDeclQualifier : 7;
 
   // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
-  unsigned PropertyAttributes : 15;
+  unsigned PropertyAttributes : 16;
 
   unsigned Nullability : 2;
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -987,6 +987,16 @@
   "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 methods "
+  "declared in an Objective-C protocol">;
+def err_objc_direct_missing_on_decl : Error<
+  "method implementation is direct but its declaration was not">;
+def err_objc_direct_on_override : Error<
+  "method overrides or implementing protocol conformance cannot be direct">;
+def err_objc_override_direct_method : Error<
+  "cannot override direct methods">;
+
 def warn_conflicting_overriding_ret_types : Warning<
   "conflicting return type in "
   "declaration of %0%diff{: $ vs $|}1,2">,
@@ -1072,6 +1082,7 @@
   "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">;
@@ -1307,6 +1318,8 @@
   "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">;
@@ -1320,6 +1333,8 @@
 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">;
 
 // C++ declarations
 def err_static_assert_expression_is_not_constant : Error<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -3897,6 +3897,48 @@
   }];
 }
 
+def ObjCDirectDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{
+This attribute can be added on methods declaration and implementation,
+categories, or extensions (for the latter two it applies the attribute
+to all methods declared in the category/implementation),
+
+This attribute can be added to an Objective-C method to mark it as being
+called directly. It can also be used on Objective-C Categories and Extensions,
+in which case it applies to all methods declared in such an ``@interface``.
+
+Properties can also be declared with the ``direct`` property attribute
+which causes the property getter and setter to be direct methods as well.
+
+Direct methods are called with the C calling conventions and do not emit
+any Objective-C metadata visible to the Objective-C runtime. Such functions
+are restricted to the current image and cannot be used cross image.
+
+Some sematics of the message send are however preserved:
+
+- messaging ``nil``,
+
+- class lazy initialization (``+initialize`` is called prior to any other
+  message making it through),
+
+- the implicit ``self`` and ``_cmd`` arguments are preserved.
+
+Note, the ``objc_direct_members`` can be used on ``@implementation`` blocks
+to automatically mark methods direct, when they are not declared in any
+``@interface`` or ``@protocol`` this class conforms to.
+  }];
+}
+
+def ObjCDirectMembersDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{
+This attribute can decorage classes ``@implementation`` blocks,
+and causes any method that has not been declared in any ``@interface``
+or ``@protocol`` this class conforms to to be direct methods.
+  }];
+}
+
 def SelectAnyDocs : Documentation {
   let Category = DocCatDecl;
   let Content = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1852,6 +1852,20 @@
   let Documentation = [Undocumented];
 }
 
+def ObjCDirect : Attr {
+  let Spellings = [Clang<"objc_direct">];
+  let Subjects = SubjectList<[ObjCMethod, ObjCCategory], ErrorDiag>;
+  let LangOpts = [ObjC];
+  let Documentation = [ObjCDirectDocs];
+}
+
+def ObjCDirectMembers : Attr {
+  let Spellings = [Clang<"objc_direct_members">];
+  let Subjects = SubjectList<[ObjCImpl], ErrorDiag>;
+  let LangOpts = [ObjC];
+  let Documentation = [ObjCDirectMembersDocs];
+}
+
 def ObjCRuntimeName : Attr {
   let Spellings = [Clang<"objc_runtime_name">];
   let Subjects = SubjectList<[ObjCInterface, ObjCProtocol], ErrorDiag>;
Index: clang/include/clang/AST/DeclObjC.h
===================================================================
--- clang/include/clang/AST/DeclObjC.h
+++ clang/include/clang/AST/DeclObjC.h
@@ -408,7 +408,7 @@
   /// \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; }
@@ -466,6 +466,9 @@
     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
@@ -747,13 +750,14 @@
     /// 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 };
@@ -876,6 +880,7 @@
 
   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 :
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to