Index: clang/include/clang/AST/DeclObjC.h =================================================================== --- clang/include/clang/AST/DeclObjC.h +++ clang/include/clang/AST/DeclObjC.h @@ -410,7 +410,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; } @@ -476,6 +476,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 @@ -757,13 +760,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 }; @@ -886,6 +890,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 : Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1861,6 +1861,20 @@ 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>; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3912,6 +3912,104 @@ }]; } +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 = [{ Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -988,6 +988,22 @@ "string is ill-formed as UTF-8 and will become a null %0 when boxed">, InGroup; +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; +def warn_objc_direct_property_ignored : Warning< + "direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">, + InGroup; + def warn_conflicting_overriding_ret_types : Warning< "conflicting return type in " "declaration of %0%diff{: $ vs $|}1,2">, @@ -1073,6 +1089,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">; @@ -1308,6 +1325,8 @@ "several methods with selector %0 of mismatched types are found " "for the @selector expression">, InGroup, 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">; @@ -1321,6 +1340,12 @@ def warn_messaging_unqualified_id : Warning< "messaging unqualified id">, DefaultIgnore, InGroup>; +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< Index: clang/include/clang/Basic/ObjCRuntime.h =================================================================== --- clang/include/clang/Basic/ObjCRuntime.h +++ clang/include/clang/Basic/ObjCRuntime.h @@ -446,6 +446,20 @@ 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. /// 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/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/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(); +} + bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const { return getMethodFamily() == OMF_init && hasAttr(); @@ -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/lib/AST/DeclPrinter.cpp =================================================================== --- clang/lib/AST/DeclPrinter.cpp +++ clang/lib/AST/DeclPrinter.cpp @@ -1461,6 +1461,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/JSONNodeDumper.cpp =================================================================== --- clang/lib/AST/JSONNodeDumper.cpp +++ clang/lib/AST/JSONNodeDumper.cpp @@ -1017,6 +1017,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/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/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ clang/lib/CodeGen/CGObjC.cpp @@ -430,6 +430,20 @@ 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 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. @@ -611,16 +625,9 @@ method); } else { // Call runtime methods directly if we can. - if (Optional 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 @@ -683,7 +690,13 @@ 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()); @@ -696,6 +709,14 @@ 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() && Index: clang/lib/CodeGen/CGObjCGNU.cpp =================================================================== --- clang/lib/CodeGen/CGObjCGNU.cpp +++ clang/lib/CodeGen/CGObjCGNU.cpp @@ -606,6 +606,9 @@ 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; @@ -3871,6 +3874,13 @@ 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/CGObjCMac.cpp =================================================================== --- clang/lib/CodeGen/CGObjCMac.cpp +++ clang/lib/CodeGen/CGObjCMac.cpp @@ -874,6 +874,10 @@ /// this translation unit. llvm::DenseMap MethodDefinitions; + /// DirectMethodDefinitions - map of direct methods which have been defined in + /// this translation unit. + llvm::DenseMap DirectMethodDefinitions; + /// PropertyNames - uniqued method variable names. llvm::DenseMap 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,13 @@ 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 +1584,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(CGF.CurFuncDecl)) - if (MD->isInstanceMethod()) + if (MD->isInstanceMethod() && !MD->isDirectMethod()) if (const ObjCInterfaceDecl *ID = MD->getClassInterface()) return IV->getContainingInterface()->isSuperClassOf(ID); return false; @@ -2103,10 +2118,9 @@ 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), - ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, - true, CallArgs, Method, Class, ObjCTypes); + return EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), + ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class, + ObjCTypes); } /// Generate code for a message send expression. @@ -2118,10 +2132,9 @@ const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method) { - return EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), - Receiver, CGF.getContext().getObjCIdType(), - false, CallArgs, Method, Class, ObjCTypes); + return EmitMessageSend(CGF, Return, ResultType, Sel, Receiver, + CGF.getContext().getObjCIdType(), false, CallArgs, + Method, Class, ObjCTypes); } static bool isWeakLinkedClass(const ObjCInterfaceDecl *ID) { @@ -2137,7 +2150,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, - llvm::Value *Sel, + Selector Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, @@ -2145,11 +2158,24 @@ const ObjCMethodDecl *Method, const ObjCInterfaceDecl *ClassReceiver, const ObjCCommonTypesHelper &ObjCTypes) { + CodeGenTypes &Types = CGM.getTypes(); + auto selTy = CGF.getContext().getObjCSelType(); + llvm::Value *SelValue; + + if (Method && Method->isDirectMethod()) { + // Direct methods will synthesize the proper `_cmd` internally, + // so just don't bother with setting the `_cmd` argument. + assert(!IsSuper); + SelValue = llvm::UndefValue::get(Types.ConvertType(selTy)); + } else { + SelValue = GetSelector(CGF, Sel); + } + 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()); + ActualArgs.add(RValue::get(SelValue), selTy); ActualArgs.addFrom(CallArgs); // If we're calling a method, use the formal signature. @@ -2190,7 +2216,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 +3325,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 +3402,8 @@ }; SmallVector 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,11 +3585,14 @@ }; SmallVector 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) { + if (PID->getPropertyDecl()->isDirectProperty()) + continue; if (ObjCMethodDecl *MD = PID->getGetterMethodDecl()) if (GetMethodDefinition(MD)) Methods[InstanceMethods].push_back(MD); @@ -3957,7 +3991,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); @@ -3968,6 +4003,34 @@ 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); @@ -3975,15 +4038,98 @@ llvm::FunctionType *MethodTy = Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD)); llvm::Function *Method = - llvm::Function::Create(MethodTy, - llvm::GlobalValue::InternalLinkage, - Name.str(), - &CGM.getModule()); - MethodDefinitions.insert(std::make_pair(OMD, Method)); + llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage, + Name.str(), &CGM.getModule()); + 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 selfAddr = CGF.GetAddrOfLocalVar(OMD->getSelfDecl()); + auto selfValue = Builder.CreateLoad(selfAddr); + + // 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 = cast(CD); + assert(OID && + "GenerateDirectMethod() should be called with the Class Interface"); + Selector SelfSel = GetNullarySelector("self", CGM.getContext()); + auto ResultType = CGF.getContext().getObjCIdType(); + RValue result; + CallArgList Args; + + // TODO: If this method is inlined, the caller might know that `self` is + // already initialized; for example, it might be an ordinary Objective-C + // method which always receives an initialized `self`, or it might have just + // forced initialization on its own. + // + // We should find a way to eliminate this unnecessary initialization in such + // cases in LLVM. + result = GeneratePossiblySpecializedMessageSend( + CGF, ReturnValueSlot(), ResultType, SelfSel, selfValue, Args, OID, + nullptr, true); + Builder.CreateStore(result.getScalarVal(), selfAddr); + + // Nullable `Class` expressions cannot be messaged with a direct method + // so the only reason why the receive can be null would be because + // of weak linking. + ReceiverCanBeNull = 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(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()) { + 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, @@ -6226,10 +6372,12 @@ SmallVector 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); } values.add(emitMethodList(ID->getObjCRuntimeNameAsString(), @@ -6550,6 +6698,8 @@ SmallVector instanceMethods; SmallVector classMethods; for (const auto *MD : OCD->methods()) { + if (MD->isDirectMethod()) + continue; if (MD->isInstanceMethod()) { instanceMethods.push_back(MD); } else { @@ -7218,8 +7368,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); } @@ -7450,8 +7599,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); } Index: clang/lib/CodeGen/CGObjCRuntime.h =================================================================== --- clang/lib/CodeGen/CGObjCRuntime.h +++ clang/lib/CodeGen/CGObjCRuntime.h @@ -169,6 +169,21 @@ const ObjCInterfaceDecl *Class = nullptr, const ObjCMethodDecl *Method = nullptr) = 0; + /// Generate an Objective-C message send operation. + /// + /// This variant allows for the call to be substituted with an optimized + /// variant. + CodeGen::RValue + GeneratePossiblySpecializedMessageSend(CodeGenFunction &CGF, + ReturnValueSlot Return, + QualType ResultType, + Selector Sel, + llvm::Value *Receiver, + const CallArgList& Args, + const ObjCInterfaceDecl *OID, + 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 +220,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/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/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2600,6 +2600,29 @@ 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(D->getDeclContext())) { + S.Diag(AL.getLoc(), diag::err_objc_direct_on_protocol) << false; + return; + } + + if (S.getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + handleSimpleAttribute(S, D, AL); + } else { + S.Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL; + } +} + +static void handleObjCDirectMembersAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + if (S.getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + handleSimpleAttribute(S, D, AL); + } else { + S.Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL; + } +} + static void handleObjCMethodFamilyAttr(Sema &S, Decl *D, const ParsedAttr &AL) { const auto *M = cast(D); if (!AL.isArgIdent(0)) { @@ -6944,6 +6967,13 @@ case ParsedAttr::AT_ObjCRootClass: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCDirect: + handleObjCDirectAttr(S, D, AL); + break; + case ParsedAttr::AT_ObjCDirectMembers: + handleObjCDirectMembersAttr(S, D, AL); + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCNonLazyClass: handleSimpleAttribute(S, D, AL); break; Index: clang/lib/Sema/SemaDeclObjC.cpp =================================================================== --- clang/lib/Sema/SemaDeclObjC.cpp +++ clang/lib/Sema/SemaDeclObjC.cpp @@ -3239,6 +3239,9 @@ if (left->isHidden() || right->isHidden()) return false; + if (left->isDirectMethod() != right->isDirectMethod()) + return false; + if (getLangOpts().ObjCAutoRefCount && (left->hasAttr() != right->hasAttr() || @@ -3430,6 +3433,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; @@ -4339,6 +4345,18 @@ }; } // end anonymous namespace +void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method, + ObjCMethodDecl *overridden) { + if (const auto *attr = overridden->getAttr()) { + Diag(method->getLocation(), diag::err_objc_override_direct_method); + Diag(attr->getLocation(), diag::note_previous_declaration); + } else if (const auto *attr = method->getAttr()) { + Diag(attr->getLocation(), diag::err_objc_direct_on_override) + << isa(overridden->getDeclContext()); + Diag(overridden->getLocation(), diag::note_previous_declaration); + } +} + void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC) { @@ -4357,8 +4375,8 @@ if (isa(overridden->getDeclContext()) || CurrentClass != overridden->getClassInterface() || overridden->isOverriding()) { + CheckObjCMethodDirectOverrides(ObjCMethod, overridden); hasOverriddenMethodsInBaseOrProtocol = true; - } else if (isa(ObjCMethod->getDeclContext())) { // OverrideSearch will return as "overridden" the same method in the // interface. For hasOverriddenMethodsInBaseOrProtocol, we need to @@ -4382,6 +4400,7 @@ for (ObjCMethodDecl *SuperOverridden : overrides) { if (isa(SuperOverridden->getDeclContext()) || CurrentClass != SuperOverridden->getClassInterface()) { + CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden); hasOverriddenMethodsInBaseOrProtocol = true; overridden->setOverriding(true); break; @@ -4489,6 +4508,12 @@ method->getLocation())); } + if (!method->isDirectMethod()) + if (const auto *attr = prevMethod->getAttr()) { + method->addAttr( + ObjCDirectAttr::CreateImplicit(S.Context, attr->getLocation())); + } + // Merge nullability of the result type. QualType newReturnType = mergeTypeNullabilityForRedecl( @@ -4719,6 +4744,12 @@ if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(), ObjCMethod->isInstanceMethod())) { mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD); + if (const auto *attr = ObjCMethod->getAttr()) { + 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(ImpDecl) && IMD->isOverriding() && @@ -4726,6 +4757,9 @@ Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category) << ObjCMethod->getDeclName(); } + } else if (ImpDecl->hasAttr()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); } // Warn if a method declared in a protocol to which a category or @@ -4745,6 +4779,11 @@ } } } else { + if (!ObjCMethod->isDirectMethod() && + ClassDecl->hasAttr()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); + } cast(ClassDecl)->addDecl(ObjCMethod); } Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -1170,6 +1170,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, @@ -1192,9 +1221,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 && @@ -2767,9 +2805,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. @@ -2971,6 +3006,30 @@ (Method && Method->getMethodFamily() == OMF_init) ? getEnclosingFunction() : nullptr; + if (Method && Method->isDirectMethod()) { + if (ReceiverType->isObjCIdType() && !isImplicit) { + Diag(Receiver->getExprLoc(), + diag::err_messaging_unqualified_id_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + if (ReceiverType->isObjCClassType() && !isImplicit) { + Diag(Receiver->getExprLoc(), + diag::err_messaging_class_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + if (SuperLoc.isValid()) { + Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + } else if (ReceiverType->isObjCIdType() && !isImplicit) { + Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id); + } + if (DIFunctionScopeInfo && DIFunctionScopeInfo->ObjCIsDesignatedInit && (SuperLoc.isValid() || isSelfExpr(Receiver))) { 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,21 @@ 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) || + CDecl->hasAttr()) { + if (isa(CDecl)) { + Diag(PDecl->getLocation(), diag::err_objc_direct_on_protocol) << true; + } else if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_direct); + } else { + Diag(PDecl->getLocation(), diag::warn_objc_direct_property_ignored) + << PDecl->getDeclName(); + } + } + return PDecl; } @@ -2460,6 +2474,9 @@ AddPropertyAttrs(*this, GetterMethod, property); + if (property->isDirectProperty()) + GetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc)); + if (property->hasAttr()) GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context, Loc)); @@ -2534,6 +2551,9 @@ AddPropertyAttrs(*this, SetterMethod, property); + if (property->isDirectProperty()) + SetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc)); + CD->addDecl(SetterMethod); if (const SectionAttr *SA = property->getAttr()) SetterMethod->addAttr(SectionAttr::CreateImplicit( Index: clang/test/CodeGenObjC/direct-method.m =================================================================== --- /dev/null +++ clang/test/CodeGenObjC/direct-method.m @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %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 +// CHECK-LABEL: define hidden i32 @"\01-[Root getInt]"( +- (int)getInt __attribute__((objc_direct)) { + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[RETVAL:%.*]] = alloca + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // self nil-check + // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]], + // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null + // CHECK-NEXT: br i1 [[NILCHECK]], + + // setting return value to nil + // CHECK-LABEL: objc_direct_method.self_is_nil: + // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0, + // CHECK-NEXT: br label + + // set value + // CHECK-LABEL: objc_direct_method.cont: + // CHECK: store{{.*}}[[RETVAL]], + // CHECK-NEXT: br label + + // return + // CHECK-LABEL: return: + // CHECK: {{%.*}} = load{{.*}}[[RETVAL]], + // CHECK-NEXT: ret + return 42; +} + +// CHECK-LABEL: define hidden i32 @"\01+[Root classGetInt]"( ++ (int)classGetInt __attribute__((objc_direct)) { + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca i8*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store i8* %{{.*}}, i8** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // [self self] + // CHECK-NEXT: [[SELF:%.*]] = load i8*, i8** [[SELFADDR]], + // CHECK-NEXT: [[SELFSEL:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[SELF0:%.*]] = call {{.*}} @objc_msgSend + // CHECK-NEXT: store i8* [[SELF0]], i8** [[SELFADDR]], + + // return + // CHECK-NEXT: ret + return 42; +} + +// CHECK-LABEL: define hidden i64 @"\01-[Root getComplex]"( +- (struct my_complex_struct)getComplex __attribute__((objc_direct)) { + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[RETVAL:%.*]] = alloca + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // self nil-check + // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]], + // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null + // CHECK-NEXT: br i1 [[NILCHECK]], + + // setting return value to nil + // CHECK-LABEL: objc_direct_method.self_is_nil: + // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0, + // CHECK-NEXT: br label + + // set value + // CHECK-LABEL: objc_direct_method.cont: + // CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memcpy{{[^(]*}}({{[^,]*}}[[RET1]], + // CHECK-NEXT: br label + + // return + // CHECK-LABEL: return: + // CHECK: [[RET2:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: {{%.*}} = load{{.*}}[[RET2]], + // CHECK-NEXT: ret + struct my_complex_struct st = {.a = 42}; + return st; +} + +// CHECK-LABEL: define hidden i64 @"\01+[Root classGetComplex]"( ++ (struct my_complex_struct)classGetComplex __attribute__((objc_direct)) { + struct my_complex_struct st = {.a = 42}; + return st; + // CHECK: ret i64 +} + +// CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"( +- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) { + // CHECK: %struct.my_aggregate_struct* noalias sret [[RETVAL:%[^,]*]], + + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // self nil-check + // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]], + // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null + // CHECK-NEXT: br i1 [[NILCHECK]], + + // setting return value to nil + // CHECK-LABEL: objc_direct_method.self_is_nil: + // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0, + // CHECK-NEXT: br label + + // set value + // CHECK-LABEL: objc_direct_method.cont: + // CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK: br label + + // return + // CHECK-LABEL: return: + // CHECK: ret void + struct my_aggregate_struct st = {.a = 42}; + return st; +} + +// CHECK-LABEL: define hidden void @"\01+[Root classGetAggregate]"( ++ (struct my_aggregate_struct)classGetAggregate __attribute__((objc_direct)) { + struct my_aggregate_struct st = {.a = 42}; + return st; + // CHECK: ret void +} + +@end + +@interface Foo : Root { + id __strong _cause_cxx_destruct; +} +@property(nonatomic, readonly, direct) int getDirect_setDynamic; +@property(nonatomic, readonly) int getDynamic_setDirect; +@end + +@interface Foo () +@property(nonatomic, readwrite) int getDirect_setDynamic; +@property(nonatomic, readwrite, direct) int getDynamic_setDirect; +@end + +__attribute__((objc_direct_members)) +@implementation Foo +// CHECK-LABEL: define hidden i32 @"\01-[Foo getDirect_setDynamic]"( +// CHECK-LABEL: define internal void @"\01-[Foo setGetDirect_setDynamic:]"( +// CHECK-LABEL: define internal i32 @"\01-[Foo getDynamic_setDirect]"( +// CHECK-LABEL: define hidden void @"\01-[Foo setGetDynamic_setDirect:]"( +// CHECK-LABEL: define internal void @"\01-[Foo .cxx_destruct]"( +@end 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 @@ -105,6 +105,8 @@ // CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record) // CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method) +// CHECK-NEXT: ObjCDirect (SubjectMatchRule_objc_method) +// CHECK-NEXT: ObjCDirectMembers (SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_category) // 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/SemaObjC/method-direct-properties.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/method-direct-properties.m @@ -0,0 +1,126 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wselector-type-mismatch %s + +@protocol ProtoDirectFail +@property(nonatomic, direct) int protoProperty; // expected-error {{'objc_direct' attribute cannot be applied to properties declared in an Objective-C protocol}} +@end + +__attribute__((objc_root_class)) +@interface Root +@property(nonatomic, direct) int propertyWithNonDirectGetter; // expected-note {{previous declaration is here}} +- (int)propertyWithNonDirectGetter; +- (int)propertyWithNonDirectGetter2; +- (int)propertyWithNonDirectGetterInParent; +- (int)propertyWithNonDirectGetterInParent2; + +@property(nonatomic, readonly, direct) int getDirect_setDynamic; // expected-note {{previous declaration is here}} +@property(nonatomic, readonly, direct) int getDirect_setDirect; // expected-note {{previous declaration is here}} +@property(nonatomic, readonly, direct) int getDirect_setDirectMembers; // expected-note {{previous declaration is here}} + +@property(nonatomic, readonly) int getDynamic_setDirect; +@property(nonatomic, readonly) int getDynamic_setDirectMembers; + +@property(nonatomic, readonly) int dynamicProperty; +@property(nonatomic, readonly) int synthDynamicProperty; + +@property(nonatomic, readonly, direct) int directProperty; // expected-note {{previous declaration is here}} +@property(nonatomic, readonly, direct) int synthDirectProperty; // expected-note {{previous declaration is here}} +@end + +__attribute__((objc_direct_members)) +@interface +Root() +@property(nonatomic) int propertyWithNonDirectGetter2; // expected-note {{previous declaration is here}} + +@property(nonatomic, readwrite) int getDirect_setDirectMembers; // expected-note {{previous declaration is here}} +@property(nonatomic, readwrite) int getDynamic_setDirectMembers; // expected-note {{previous declaration is here}} +@end + +@interface Root () +@property(nonatomic, readwrite) int getDirect_setDynamic; +@property(nonatomic, readwrite, direct) int getDirect_setDirect; // expected-note {{previous declaration is here}} + +@property(nonatomic, readwrite, direct) int getDynamic_setDirect; // expected-note {{previous declaration is here}} +@end + +@interface Sub : Root +@property(nonatomic, direct) int propertyWithNonDirectGetterInParent; // expected-note {{previous declaration is here}} + +- (int)propertyWithNonDirectGetter; // no error: legal override +- (int)propertyWithNonDirectGetter2; // no error: legal override +- (int)propertyWithNonDirectGetterInParent; // no error: legal override +- (int)propertyWithNonDirectGetterInParent2; // no error: legal override + +@end + +__attribute__((objc_direct_members)) +@interface Sub () +@property(nonatomic) int propertyWithNonDirectGetterInParent2; // expected-note {{previous declaration is here}} +@end + +// make sure that the `directness` of methods stuck, +// by observing errors trying to override the setter +@interface SubWitness : Sub + +- (int)setPropertyWithNonDirectGetter:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)setPropertyWithNonDirectGetter2:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)setPropertyWithNonDirectGetterInParent:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)setPropertyWithNonDirectGetterInParent2:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} + +- (int)getDirect_setDynamic; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)setGetDirect_setDynamic:(int)value; +- (int)getDirect_setDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)setGetDirect_setDirect:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)getDirect_setDirectMembers; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)setGetDirect_setDirectMembers:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} + +- (int)getDynamic_setDirect; +- (int)setGetDynamic_setDirect:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (int)getDynamic_setDirectMembers; +- (int)setGetDynamic_setDirectMembers:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}} +@end + +__attribute__((objc_direct_members)) +@implementation Root +- (int)propertyWithNonDirectGetter { + return 42; +} +- (int)propertyWithNonDirectGetter2 { + return 42; +} +- (int)propertyWithNonDirectGetterInParent { + return 42; +} +- (int)propertyWithNonDirectGetterInParent2 { + return 42; +} + +- (int)dynamicProperty { + return 42; +} +- (int)directProperty { + return 42; +} +@end + +@implementation Sub +- (int)propertyWithNonDirectGetter { + return 42; +} +- (int)propertyWithNonDirectGetter2 { + return 42; +} + +- (int)dynamicProperty { + return 42; +} +- (int)synthDynamicProperty { + return 42; +} + +- (int)directProperty { // expected-error {{cannot override a method that is declared direct by a superclass}} + return 42; +} +- (int)synthDirectProperty { // expected-error {{cannot override a method that is declared direct by a superclass}} + return 42; +} +@end Index: clang/test/SemaObjC/method-direct.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/method-direct.m @@ -0,0 +1,148 @@ +// 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)otherRootDirect __attribute__((objc_direct)); // expected-note {{direct method 'otherRootDirect' declared here}} ++ (void)otherClassRootDirect __attribute__((objc_direct)); // expected-note {{direct method 'otherClassRootDirect' declared here}} +- (void)notDirectInIface; // expected-note {{previous declaration is here}} ++ (void)classNotDirectInIface; // expected-note {{previous declaration is here}} +@end + +__attribute__((objc_direct_members)) +@interface Root () +- (void)rootExtensionDirect; // expected-note {{previous declaration is here}} ++ (void)classRootExtensionDirect; // expected-note {{previous declaration is here}} +@end + +__attribute__((objc_direct_members)) +@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_members)) // expected-error {{'objc_direct_members' attribute only applies to Objective-C implementation declarations and Objective-C containers}} +@interface SubDirectFail : Root +- (instancetype)init; +@end + +@interface Sub : Root +/* invalid overrides with directs */ +- (void)rootRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}} ++ (void)classRootRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}} +- (void)protoMethod __attribute__((objc_direct)); // expected-error {{methods that implement protocol requirements cannot be direct}} ++ (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{methods that implement protocol requirements cannot be direct}} +- (void)rootExtensionRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}} ++ (void)classRootExtensionRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}} +- (void)rootCategoryRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}} ++ (void)classRootCategoryRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}} + +/* invalid overrides of directs */ +- (void)rootDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootExtensionDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootExtensionDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootExtensionDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootExtensionDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootCategoryDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootCategoryDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootCategoryDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootCategoryDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} +@end + +__attribute__((objc_direct_members)) +@implementation Root +- (void)rootRegular { +} ++ (void)classRootRegular { +} +- (void)rootDirect { +} ++ (void)classRootDirect { +} +- (void)otherRootDirect { +} ++ (void)otherClassRootDirect { +} +- (void)rootExtensionDirect { +} ++ (void)classRootExtensionDirect { +} +- (void)rootExtensionRegular { +} ++ (void)classRootExtensionRegular { +} +- (void)rootExtensionDirect2 { +} ++ (void)classRootExtensionDirect2 { +} +- (void)notDirectInIface __attribute__((objc_direct)) // expected-error {{direct method implementation was previously declared not direct}} +{ +} ++ (void)classNotDirectInIface __attribute__((objc_direct)) // expected-error {{direct method implementation was previously declared not direct}} +{ +} +- (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 + +@interface ValidSub : Root +@end + +@implementation ValidSub +- (void)someValidSubMethod { + [super otherRootDirect]; // expected-error {{messaging super with a direct method}} +} +@end + +extern void callMethod(id obj, Class cls); +extern void useSel(SEL sel); + +void callMethod(id obj, Class cls) { + [Root otherClassRootDirect]; + [cls otherClassRootDirect]; // expected-error {{messaging a Class with a method that is possibly direct}} + [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}} +}