diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -487,6 +487,9 @@ /// True if the method is tagged as objc_direct bool isDirectMethod() const; + /// True if the method is tagged as objc_direct and objc_direct_visible + bool isDirectMethodVisible() const; + /// True if the method has a parameter that's destroyed in the callee. bool hasParamDestroyedInCallee() const; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2262,6 +2262,13 @@ let Documentation = [ObjCDirectMembersDocs]; } +def ObjCDirectVisible : Attr { + let Spellings = [Clang<"objc_direct_visible">]; + let Subjects = SubjectList<[ObjCMethod], ErrorDiag>; + let LangOpts = [ObjC]; + let Documentation = [ObjCDirectVisibleDocs]; +} + def ObjCNonRuntimeProtocol : Attr { let Spellings = [Clang<"objc_non_runtime_protocol">]; let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5406,6 +5406,19 @@ }]; } +def ObjCDirectVisibleDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute specifies that the ``objc_direct`` method it is placed on is to +be vislble (ie skip marking for hidden linkage) so that it can be called across +link unit boundaries. If the ``objc_direct`` attribute is not set manually this +also enables the attribute (since it would not makesense to do otherwise). When +an ObjC method is marked visible its internal name is scrubed of characters that +cause problems with linkage or do not conform to ABI +(ie ``\0-[a: ]`` becomes ``-``). + }]; +} + def ObjCNonRuntimeProtocolDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -838,6 +838,10 @@ !getASTContext().getLangOpts().ObjCDisableDirectMethodsForTesting; } +bool ObjCMethodDecl::isDirectMethodVisible() const { + return isDirectMethod() && hasAttr(); +} + bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const { return getMethodFamily() == OMF_init && hasAttr(); diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -359,10 +359,16 @@ } // \01+[ContainerName(CategoryName) SelectorName] + // Note: If we are mangling for an objc_direct method with external visibility + // then the standard mangling scheme will not work. We must replace the + // square braces with angle braces so in the case of visible direct objc + // methods it results in: + + // This will include a prefix _ on Darwin. if (includePrefixByte) { OS << '\01'; } - OS << (MD->isInstanceMethod() ? '-' : '+') << '['; + OS << (MD->isInstanceMethod() ? '-' : '+'); + OS << (MD->isDirectMethodVisible() ? '<' : '['); if (const auto *CID = MD->getCategory()) { OS << CID->getClassInterface()->getName(); if (includeCategoryNamespace) { @@ -376,7 +382,7 @@ } OS << ' '; MD->getSelector().print(OS); - OS << ']'; + OS << (MD->isDirectMethodVisible() ? '>' : ']'); } void MangleContext::mangleObjCMethodNameAsSourceName(const ObjCMethodDecl *MD, diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -760,7 +760,9 @@ const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD); if (OMD->isDirectMethod()) { - Fn->setVisibility(llvm::Function::HiddenVisibility); + Fn->setVisibility(OMD->isDirectMethodVisible() + ? llvm::Function::DefaultVisibility + : llvm::Function::HiddenVisibility); CGM.SetLLVMFunctionAttributes(OMD, FI, Fn, /*IsThunk=*/false); CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn); } else { diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -474,7 +474,7 @@ std::string buffer; llvm::raw_string_ostream out(buffer); CGM.getCXXABI().getMangleContext().mangleObjCMethodName(OMD, out, - /*includePrefixByte=*/true, + /*includePrefixByte=*/!OMD->isDirectMethodVisible(), includeCategoryName); return buffer; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2943,6 +2943,22 @@ } } +static void handleObjCDirectVisibleAttr(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()) { + if (!D->hasAttr()) + handleSimpleAttribute(S, D, AL); + 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)) { @@ -8783,6 +8799,9 @@ case ParsedAttr::AT_ObjCDirect: handleObjCDirectAttr(S, D, AL); break; + case ParsedAttr::AT_ObjCDirectVisible: + handleObjCDirectVisibleAttr(S, D, AL); + break; case ParsedAttr::AT_ObjCDirectMembers: handleObjCDirectMembersAttr(S, D, AL); handleSimpleAttribute(S, D, AL); diff --git a/clang/test/CodeGenObjC/objc-direct-wrapper.m b/clang/test/CodeGenObjC/objc-direct-wrapper.m new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenObjC/objc-direct-wrapper.m @@ -0,0 +1,50 @@ +// RUN: %clang -fobjc-arc -Wno-objc-root-class -ObjC -fobjc-runtime=ios -FFoundation \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 -c -o - %s | \ +// RUN: llvm-nm - | FileCheck -check-prefix=CHECK-WRAPPER %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s | llvm-nm - | \ +// RUN: FileCheck -check-prefix=CHECK-DEFAULT %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DEFINE %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class -DNO_OBJC_IMPL \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DECLARE %s + +// CHECK-WRAPPER: T _- + // TODO: Fix this +// CHECK-DEFAULT: t -[C testMethod:bar:] +// CHECK-WRAPPER-IR-DEFINE: define {{(dso_local )?}}void @"-" +// CHECK-WRAPPER-IR-DECLARE: declare {{(dso_local )?}}void @"-" + +#if ENABLE_VISIBLE_OBJC_DIRECT +#define OBJC_DIRECT __attribute((objc_direct)) __attribute__((objc_direct_visible)) +#else +#define OBJC_DIRECT +#endif + +@interface C +- (void)testMethod:(int)arg1 bar:(float)arg2 OBJC_DIRECT; +@end + +#ifndef NO_OBJC_IMPL +@implementation C +- (void)testMethod:(int)arg1 bar:(float)arg2 OBJC_DIRECT { +} +@end +#endif + +C *c; + +void f() { + [c testMethod:1 bar:1.0]; +}