| Issue |
56034
|
| Summary |
Add support for _objc_msgSend stubs from Xcode 14
|
| Labels |
lld:MachO
|
| Assignees |
keith
|
| Reporter |
keith
|
In Xcode 14 / ld 816 Apple implemented a size / performance optimization where they deduplicate _objc_msgSend setup infrastructure by Objective-C selectors. This comes in the form of undefined symbols in each object file:
```objc
@import Foundation;
@interface Foo : NSObject
@end
@implementation Foo
- (void)bar {
NSLog(@"%@", self);
}
@end
int main() {
Foo *foo = [[Foo alloc] init];
[foo bar];
return 0;
}
```
```
% DEVELOPER_DIR=/Applications/Xcode-14.0.0-beta1.app clang foo.m -o foo.o -fmodules -c
% nm easy.o | grep objc_msg
U _objc_msgSend$bar
```
Then the linker inserts these stubs during link, one for each selector string after the `$` (see the final symbol):
```
% nm -format=darwin foo
0000000100003ecc (__TEXT,__text) non-external -[Foo bar]
(undefined) external _NSLog (from Foundation)
00000001000080c8 (__DATA,__objc_data) external _OBJC_CLASS_$_Foo
(undefined) external _OBJC_CLASS_$_NSObject (from libobjc)
00000001000080a0 (__DATA,__objc_data) external _OBJC_METACLASS_$_Foo
(undefined) external _OBJC_METACLASS_$_NSObject (from libobjc)
0000000100003f80 (__TEXT,__objc_methlist) non-external __OBJC_$_INSTANCE_METHODS_Foo
0000000100008048 (__DATA,__objc_const) non-external __OBJC_CLASS_RO_$_Foo
0000000100008000 (__DATA,__objc_const) non-external __OBJC_METACLASS_RO_$_Foo
(undefined) external ___CFConstantStringClassReference (from CoreFoundation)
0000000100000000 (__TEXT,__text) [referenced dynamically] external __mh_execute_header
(undefined) external __objc_empty_cache (from libobjc)
0000000100003f04 (__TEXT,__text) external _main
(undefined) external _objc_alloc_init (from libobjc)
(undefined) external _objc_msgSend (from libobjc)
0000000100003f60 (__TEXT,__objc_stubs) non-external (was a private external) _objc_msgSend$bar
```
This new feature is enabled by default in Apple clang 14, and doesn't seem to be in Apple's public fork. It can be disabled in the meantime by passing `-fno-objc-msgsend-selector-stubs` to all clang compiles.
The reason behind this feature is explained in [this WWDC session](https://developer.apple.com/wwdc22/110363) but the gist is them trying to dedup the duplicate setup code that was previously inlined for each objc_msgSend call.
The implementation of these symbols is also described in that session, the linker has 2 modes, the default mode (which is the same as passing `-objc_stubs_fast`) produces this asm on arm64:
```s
_objc_msgSend$bar:
adrp x1, #0x100008000 ; 0x100008090@PAGE, CODE XREF=_main+48
ldr x1, [x1, #0x90] ; 0x100008090@PAGEOFF, "bar",@selector(bar)
adrp x16, #0x100004000 ; 0x100004010@PAGE
ldr x16, [x16, #0x10] ; 0x100004010@PAGEOFF, _objc_msgSend_100004010,_objc_msgSend
br x16 ; _objc_msgSend
```
Where the other mode optimizing for size, controlled by passing `-objc_stubs_small` produces this asm on arm64:
```s
_objc_msgSend$bar:
adrp x1, #0x100008000 ; 0x100008090@PAGE, CODE XREF=_main+48
ldr x1, [x1, #0x90] ; 0x100008090@PAGEOFF, argument "selector" for method imp___stubs__objc_msgSend, "bar",@selector(bar)
b imp___stubs__objc_msgSend ; objc_msgSend
```
The major difference here being the extra inline instruction size, vs the extra jump to the objc_msgSend stub.
Some other various notes:
- Apple implements these stubs in the `__TEXT,__objc_stubs` section
- The addresses of the selector loads points to the selectors in the `__selrefs` section which lld does not currently implement
- Passing `-fno-objc-msgsend-selector-stubs` to all clang invocations is the only way I see to disable (note this flag is undocumented)
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs