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

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 11 07:09:57 PDT 2024


https://github.com/alx32 created https://github.com/llvm/llvm-project/pull/95124

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. 

>From b795c50a4376e40c904d64a699c5d0fa87b1166e Mon Sep 17 00:00:00 2001
From: Alex B <alexborcan at meta.com>
Date: Tue, 11 Jun 2024 06:44:28 -0700
Subject: [PATCH] [lld-macho] Add swift support to ObjC category merger

---
 lld/MachO/ObjC.cpp                            | 20 +++--
 lld/MachO/ObjC.h                              |  2 +
 .../MachO/objc-category-merging-minimal.s     | 90 +++++++++++++++++++
 3 files changed, 105 insertions(+), 7 deletions(-)

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 at 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 at 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 at 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 at 0:8":
+	.asciz	"v16 at 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 at 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



More information about the llvm-commits mailing list