Skip to content

Add a -emit-dead-strippable-symbols flag that emits functions/variable/metadata in a dead_strip-friendly way. #34281

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

Closed
wants to merge 2 commits into from

Conversation

kubamracek
Copy link
Contributor

Add a -emit-dead-strippable-symbols flag that emits functions/variable/metadata in a dead_strip-friendly way. This enables static linking to remove unused functions and classes even across modules. For now, I'm adding the flag as an opt-in experimental mode, but the goal is to eventually be able to emit symbols this way for everything. This way we could see nice code size savings when e.g. including a lot of package dependencies where not everything is actually used.

@jckarter
Copy link
Contributor

What happens if we remove even the llvm.compiler.used annotation?

…e/metadata in a dead_strip-friendly way. This enables static linking to remove unused functions and classes even across modules.
@kubamracek
Copy link
Contributor Author

Updated the PR, added more tests.

What happens if we remove even the llvm.compiler.used annotation?

Good point. I realized it's not needed for this purpose. I originally thought that it might be needed for LTO/ThinLTO, but it turns out it's not needed for that either. I dropped those changes.

@kubamracek
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 30fa52b

@kubamracek
Copy link
Contributor Author

@swift-ci please test

@kubamracek
Copy link
Contributor Author

@jckarter @airspeedswift any more feedback or this good to go in?

break;
case llvm::Triple::XCOFF:
case llvm::Triple::COFF:
sectionName = ".sw5prt$B";
Copy link
Contributor

Choose a reason for hiding this comment

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

One question I have is if this information around sectionName is something that is also somewhere else. Seems like we should have that centralized so it isn't out of sync, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it was just below? Nm.

@jckarter
Copy link
Contributor

LGTM Kuba.

@SharonXSharon
Copy link

Hi @kubamracek, thanks for putting up this patch!
We tried building a small swift app with this patch, but it crashed at launch. We then tried building it with both -emit-dead-strippable-symbols and full LTO, the app launched fine. (And it showed a significant size reduction!)
We suspect that at IR level, some symbols/metadata needed across swift modules may have been stripped due to the optimization does not have a link-unit scope view. So what do you think of adding a requirement that only use this flag when full/thin LTO is enabled?

@jckarter
Copy link
Contributor

jckarter commented Jan 6, 2021

@SharonXSharon Do you know what symbols are getting incorrectly stripped? Can we mark them used? Ideally we should always emit code this way and it wouldn't need to be a distinct mode.

@SharonXSharon
Copy link

@jckarter It looks some function static _DictionaryStorage.resize in libswiftCore.dylib though, according to the crash trace,

Thread 2 Crashed:
0   libswiftCore.dylib            	0x00000001952d24f0 static _DictionaryStorage.resize+ 828656 (original:capacity:move:) + 4
1   theSmallApp        	0x000000010117d940 0x10095c000 + 8526144
2   theSmallApp        	0x000000010117d80c 0x10095c000 + 8525836
3   theSmallApp        	0x000000010117d718 0x10095c000 + 8525592
4   theSmallApp        	0x000000010117b374 0x10095c000 + 8516468
5   theSmallApp        	0x000000010117b290 0x10095c000 + 8516240
6   theSmallApp        	0x0000000100aed770 0x10095c000 + 1644400
7   libdispatch.dylib             	0x0000000191316fd0 _dispatch_call_block_and_release + 32
8   libdispatch.dylib             	0x0000000191318ac8 _dispatch_client_callout + 20
9   libdispatch.dylib             	0x00000001913280d4 _dispatch_lane_concurrent_drain + 892
10  libdispatch.dylib             	0x00000001913207a4 _dispatch_lane_invoke + 520
11  libdispatch.dylib             	0x0000000191329104 _dispatch_root_queue_drain + 356
12  libdispatch.dylib             	0x00000001913298e8 _dispatch_worker_thread2 + 116
13  libsystem_pthread.dylib       	0x00000001d8c748cc _pthread_wqthread + 216
14  libsystem_pthread.dylib       	0x00000001d8c7b77c start_wqthread + 8

@manman-ren
Copy link

@SharonXSharon One debugging option is to dump out the symbols that get changed by this flag, and compare the existence of this set between no fullLTO vs fullLTO.
@jckarter I thought we can't always mark these symbols as unused. These symbols can be unused in this swift module but used in other swift module. If we mark them as unused, the symbols will be stripped off, while they are needed by other swift modules. In the case of fullLTO, we have all the swift modules during fullLTO, so the right set of symbols are kept. This may need Swift LTO or llvm LTO to work?

@SharonXSharon
Copy link

I thought marking things public or open means they will be used across swift modules, does this patch not honor that and it requires extra annotation for symbols used across modules?

@kubamracek
Copy link
Contributor Author

I am aware of a few problems with this change, and I don't plan on landing this as it is. I think the only way to get this dead-stripping to work correctly is by requiring LTO/ThinLTO and acknowledging that there cannot be any non-visible Swift code using any of the classes/structs/protocols defined in the module.

@SharonXSharon
Copy link

@kubamracek

I am aware of a few problems with this change

What are the other problems? Do you mean they can all be solved by bundling this flag with LTO/thinLTO?
And is the plan to update this patch by adding some flags configuration to require LTO/thinLTO when -emit-dead-strippable-symbols is present?

@jckarter
Copy link
Contributor

jckarter commented Jan 7, 2021

@jckarter I thought we can't always mark these symbols as unused. These symbols can be unused in this swift module but used in other swift module. If we mark them as unused, the symbols will be stripped off, while they are needed by other swift modules. In the case of fullLTO, we have all the swift modules during fullLTO, so the right set of symbols are kept. This may need Swift LTO or llvm LTO to work?

Uses between images should be limited to public and @usableFromInline interfaces. If we're linking a dylib, we'd have to give all those symbols default visibility and assume they're used. We don't want to do that for static libraries, but we ought to be able to dead-strip after a static library is linked into its consuming executable, AFAICS.

@kubamracek
Copy link
Contributor Author

What are the other problems?

The "live_support" flag is 1) only supported by ld64, so it won't work on Linux, and 2) limited and won't allow dead_stripping of protocol conformances because ld64's behavior treats multiple references from a live_support atom as a "keep live if any reference is alive", but we really want a "keep live if all references are alive" behavior.

My plan is to emit slightly different IR and rely on LTO/ThinLTO and the compiler to do the dead-stripping (in GlobalDCE) instead of the linker.

@shepting
Copy link

shepting commented Apr 9, 2021

This change is really desirable for us, are there any updates on next steps or blockers to helping this land?

@kubamracek kubamracek closed this Nov 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants