Skip to content

[lld-macho] Add swift support to ObjC category merger #95124

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 13, 2024

Conversation

alx32
Copy link
Contributor

@alx32 alx32 commented Jun 11, 2024

Currently ObjC category merger only supports categories defined in ObjC. But swift supports generating ObjC categories also. This change adds supports for the later categories.

@alx32 alx32 requested review from thevinster, kyulee-com and ellishg and removed request for thevinster and kyulee-com June 11, 2024 14:09
@alx32 alx32 marked this pull request as ready for review June 11, 2024 14:10
@llvmbot
Copy link
Member

llvmbot commented Jun 11, 2024

@llvm/pr-subscribers-lld-macho

@llvm/pr-subscribers-lld

Author: None (alx32)

Changes

Currently ObjC category merger only supports categories defined in ObjC. But swift supports generating ObjC categories also. This change adds supports for the later categories.


Full diff: https://github.com/llvm/llvm-project/pull/95124.diff

3 Files Affected:

  • (modified) lld/MachO/ObjC.cpp (+13-7)
  • (modified) lld/MachO/ObjC.h (+2)
  • (modified) lld/test/MachO/objc-category-merging-minimal.s (+90)
diff --git a/lld/MachO/ObjC.cpp b/lld/MachO/ObjC.cpp
index 6e857cfcd92f6..91fd43d50451e 100644
--- a/lld/MachO/ObjC.cpp
+++ b/lld/MachO/ObjC.cpp
@@ -679,11 +679,16 @@ void ObjcCategoryMerger::parseProtocolListInfo(const ConcatInputSection *isec,
       (protocolCount * target->wordSize) +
       /*header(count)*/ protocolListHeaderLayout.totalSize +
       /*extra null value*/ target->wordSize;
-  assert(expectedListSize == ptrListSym->isec()->data.size() &&
+
+  // On Swift, the protocol list does not have the extra (unecessary) null value
+  uint32_t expectedListSizeSwift = expectedListSize - target->wordSize;
+
+  assert((expectedListSize == ptrListSym->isec()->data.size() ||
+          expectedListSizeSwift == ptrListSym->isec()->data.size()) &&
          "Protocol list does not match expected size");
 
   // Suppress unsuded var warning
-  (void)expectedListSize;
+  (void)expectedListSize, (void)expectedListSizeSwift;
 
   uint32_t off = protocolListHeaderLayout.totalSize;
   for (uint32_t inx = 0; inx < protocolCount; ++inx) {
@@ -1148,10 +1153,9 @@ void ObjcCategoryMerger::collectAndValidateCategoriesData() {
       if (nlCategories.count(categorySym))
         continue;
 
-      // We only support ObjC categories (no swift + @objc)
-      // TODO: Support swift + @objc categories also
-      if (!categorySym->getName().starts_with(objc::symbol_names::category))
-        continue;
+      assert(categorySym->getName().starts_with(objc::symbol_names::category) ||
+             categorySym->getName().starts_with(
+                 objc::symbol_names::swift_objc_category));
 
       auto *catBodyIsec = dyn_cast<ConcatInputSection>(categorySym->isec());
       assert(catBodyIsec &&
@@ -1270,7 +1274,9 @@ void ObjcCategoryMerger::eraseMergedCategories() {
 
       eraseISec(catInfo.catBodyIsec);
 
-      tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);
+      // We can't erase 'catLayout.nameOffset' because for Swift categories, the
+      // name will be referenced in __METACLASS_DATA_.
+      // TODO: handle the above smarter
       tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
                                   catLayout.instanceMethodsOffset);
       tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
diff --git a/lld/MachO/ObjC.h b/lld/MachO/ObjC.h
index 8081605670c51..790e096caf760 100644
--- a/lld/MachO/ObjC.h
+++ b/lld/MachO/ObjC.h
@@ -32,6 +32,8 @@ constexpr const char categoryInstanceMethods[] =
 constexpr const char categoryClassMethods[] =
     "__OBJC_$_CATEGORY_CLASS_METHODS_";
 constexpr const char categoryProtocols[] = "__OBJC_CATEGORY_PROTOCOLS_$_";
+
+constexpr const char swift_objc_category[] = "__CATEGORY_";
 } // namespace symbol_names
 
 // Check for duplicate method names within related categories / classes.
diff --git a/lld/test/MachO/objc-category-merging-minimal.s b/lld/test/MachO/objc-category-merging-minimal.s
index fcd90f178b150..527493303c583 100644
--- a/lld/test/MachO/objc-category-merging-minimal.s
+++ b/lld/test/MachO/objc-category-merging-minimal.s
@@ -23,6 +23,10 @@
 # RUN: llvm-objdump --objc-meta-data --macho merge_base_class_minimal_no_merge.dylib  | FileCheck %s --check-prefixes=NO_MERGE_INTO_BASE
 # RUN: llvm-objdump --objc-meta-data --macho merge_base_class_minimal_yes_merge.dylib | FileCheck %s --check-prefixes=YES_MERGE_INTO_BASE
 
+############ Test merging swift category into the base class ############
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o MyBaseClassSwiftExtension.o MyBaseClassSwiftExtension.s
+# RUN: %lld -arch arm64 -dylib -o merge_base_class_swift_minimal_yes_merge.dylib -objc_category_merging MyBaseClassSwiftExtension.o merge_base_class_minimal.o
+# RUN: llvm-objdump --objc-meta-data --macho merge_base_class_swift_minimal_yes_merge.dylib | FileCheck %s --check-prefixes=YES_MERGE_INTO_BASE_SWIFT
 
 #### Check merge categories enabled ###
 # Check that the original categories are not there
@@ -77,6 +81,21 @@ YES_MERGE_INTO_BASE-NEXT: name {{.*}} baseInstanceMethod
 YES_MERGE_INTO_BASE-NEXT: types {{.*}} v16@0:8
 YES_MERGE_INTO_BASE-NEXT: imp -[MyBaseClass baseInstanceMethod]
 
+
+#### Check merge swift category into base class ###
+YES_MERGE_INTO_BASE_SWIFT: _OBJC_CLASS_$_MyBaseClass
+YES_MERGE_INTO_BASE_SWIFT-NEXT: _OBJC_METACLASS_$_MyBaseClass
+YES_MERGE_INTO_BASE_SWIFT: baseMethods
+YES_MERGE_INTO_BASE_SWIFT-NEXT: entsize 24
+YES_MERGE_INTO_BASE_SWIFT-NEXT: count 2
+YES_MERGE_INTO_BASE_SWIFT-NEXT: name {{.*}} swiftMethod
+YES_MERGE_INTO_BASE_SWIFT-NEXT: types {{.*}} v16@0:8
+YES_MERGE_INTO_BASE_SWIFT-NEXT: imp _$sSo11MyBaseClassC0abC14SwiftExtensionE11swiftMethodyyFTo
+YES_MERGE_INTO_BASE_SWIFT-NEXT: name {{.*}} baseInstanceMethod
+YES_MERGE_INTO_BASE_SWIFT-NEXT: types {{.*}} v16@0:8
+YES_MERGE_INTO_BASE_SWIFT-NEXT: imp -[MyBaseClass baseInstanceMethod]
+
+
 #--- a64_fakedylib.s
 
     .section    __DATA,__objc_data
@@ -279,3 +298,74 @@ L_OBJC_IMAGE_INFO:
 	.long	0
 	.long	64
 .subsections_via_symbols
+
+
+#--- MyBaseClassSwiftExtension.s
+; xcrun -sdk macosx swiftc -emit-assembly MyBaseClassSwiftExtension.swift -import-objc-header YourProject-Bridging-Header.h -o MyBaseClassSwiftExtension.s
+;  ================== Generated from Swift: ==================
+; import Foundation
+; extension MyBaseClass {
+;     @objc func swiftMethod() {
+;     }
+; }
+;  ================== Generated from Swift ===================
+	.private_extern	_$sSo11MyBaseClassC0abC14SwiftExtensionE11swiftMethodyyF
+	.globl	_$sSo11MyBaseClassC0abC14SwiftExtensionE11swiftMethodyyF
+	.p2align	2
+_$sSo11MyBaseClassC0abC14SwiftExtensionE11swiftMethodyyF:
+	.cfi_startproc
+	mov	w0, #0
+	ret
+	.cfi_endproc
+
+	.p2align	2
+_$sSo11MyBaseClassC0abC14SwiftExtensionE11swiftMethodyyFTo:
+	.cfi_startproc
+	mov	w0, #0
+	ret
+	.cfi_endproc
+
+	.section	__TEXT,__cstring,cstring_literals
+	.p2align	4, 0x0
+l_.str.25.MyBaseClassSwiftExtension:
+	.asciz	"MyBaseClassSwiftExtension"
+
+	.section	__TEXT,__objc_methname,cstring_literals
+"L_selector_data(swiftMethod)":
+	.asciz	"swiftMethod"
+
+	.section	__TEXT,__cstring,cstring_literals
+"l_.str.7.v16@0:8":
+	.asciz	"v16@0:8"
+
+	.section	__DATA,__objc_data
+	.p2align	3, 0x0
+__CATEGORY_INSTANCE_METHODS_MyBaseClass_$_MyBaseClassSwiftExtension:
+	.long	24
+	.long	1
+	.quad	"L_selector_data(swiftMethod)"
+	.quad	"l_.str.7.v16@0:8"
+	.quad	_$sSo11MyBaseClassC0abC14SwiftExtensionE11swiftMethodyyFTo
+
+	.section	__DATA,__objc_const
+	.p2align	3, 0x0
+__CATEGORY_MyBaseClass_$_MyBaseClassSwiftExtension:
+	.quad	l_.str.25.MyBaseClassSwiftExtension
+	.quad	_OBJC_CLASS_$_MyBaseClass
+	.quad	__CATEGORY_INSTANCE_METHODS_MyBaseClass_$_MyBaseClassSwiftExtension
+	.quad	0
+	.quad	0
+	.quad	0
+	.quad	0
+	.long	60
+	.space	4
+
+	.section	__DATA,__objc_catlist,regular,no_dead_strip
+	.p2align	3, 0x0
+_objc_categories:
+	.quad	__CATEGORY_MyBaseClass_$_MyBaseClassSwiftExtension
+
+	.no_dead_strip	_main
+	.no_dead_strip	l_entry_point
+
+.subsections_via_symbols

Copy link

github-actions bot commented Jun 12, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@alx32 alx32 force-pushed the 03_swift_cat_merging branch from 5c39455 to 99ac21a Compare June 12, 2024 22:16
Copy link
Contributor

@kyulee-com kyulee-com left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm assuming the follow-up PR.

@alx32 alx32 merged commit 93318a8 into llvm:main Jun 13, 2024
7 checks passed
@alx32 alx32 deleted the 03_swift_cat_merging branch July 11, 2024 17:56
EthanLuisMcDonough pushed a commit to EthanLuisMcDonough/llvm-project that referenced this pull request Aug 13, 2024
Currently ObjC category merger only supports categories defined in ObjC.
But swift supports generating ObjC categories also. This change adds
supports for the later categories.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants