What are the tradeoffs of -cross-module-optimization?

In Swift 5.2 we got the flag -cross-module-optimization. What are the tradeoffs of turning this flag on for release builds? Why isn't there a dedicated option for it in the Xcode build settings?

3 Likes

Xcode is private to Apple, and they don't comment on whys and wherefores for private tools. May show up in a future release, may not. If you want, you can file an enhancement request to Feedback Assistant.

Outside of the Xcode question I'm still curious about when this would make sense to use as well.

Yeah I'm not so curious about Xcode exactly, just more pointing out that this flag isn't really being promoted as something people should add, unlike wholemodule optimization for example. So, that points to there being tradeoffs, but if so, I'm not sure what they could be.

I tried it on a large project, and got crashes for a number of invocations like this one:

0.	Running pass 'Module Verifier' on function '@"$s10MobiusCore10UnwrapEnum33_90C32D311123E16060FC94EF1F24C504LLO7extract4case4fromqd_0_Sgqd__qd_0_XE_qd__tr0_lFZ0L4HelpL_AGSaySSG_qd_0_tSgqd___tr0_0_lF23StorytellingFeatureImpl0P6EffectO_AL0B12PlayerActionOAnPTg5"'
0  swift                    0x0000000105ac6615 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 37
1  swift                    0x0000000105ac5615 llvm::sys::RunSignalHandlers() + 85
2  swift                    0x0000000105ac6bcf SignalHandler(int) + 111
3  libsystem_platform.dylib 0x00007fff701735fd _sigtramp + 29
4  libsystem_platform.dylib 0x000000010dbb5b8d _sigtramp + 18446603343160944045
5  libsystem_c.dylib        0x00007fff70049808 abort + 120
6  swift                    0x000000010162aaaf swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*)::$_1::__invoke(void*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool) + 1151
7  swift                    0x0000000105a45a1a llvm::report_fatal_error(llvm::Twine const&, bool) + 234
8  swift                    0x0000000105a45922 llvm::report_fatal_error(char const*, bool) + 50
9  swift                    0x00000001059f2bcf (anonymous namespace)::VerifierLegacyPass::runOnFunction(llvm::Function&) + 111
10 swift                    0x0000000105992abd llvm::FPPassManager::runOnFunction(llvm::Function&) + 925
11 swift                    0x00000001059922e8 llvm::legacy::FunctionPassManagerImpl::run(llvm::Function&) + 104
12 swift                    0x000000010599215b llvm::legacy::FunctionPassManager::run(llvm::Function&) + 91
13 swift                    0x00000001019e9f51 swift::performLLVM(swift::IRGenOptions const&, swift::DiagnosticEngine&, llvm::sys::SmartMutex<false>*, llvm::GlobalVariable*, llvm::Module*, llvm::TargetMachine*, swift::version::Version const&, llvm::StringRef, swift::UnifiedStatsReporter*) + 5089
14 swift                    0x00000001019f2451 (anonymous namespace)::LLVMCodeGenThreads::Thread::run() + 209
15 swift                    0x00000001019f2379 (anonymous namespace)::LLVMCodeGenThreads::runThread(void*) + 9
16 libsystem_pthread.dylib  0x00007fff7017f109 _pthread_start + 148
17 libsystem_pthread.dylib  0x00007fff7017ab8b thread_start + 15

Crashes on our project as well.

@Erik_Eckstein can talk more about this, but my understanding is that he was hoping people would try it out and file bugs. We also haven't completed expanded it to serialize everything that we want it to (we need to be careful with code-size).

Is this intended to be an alternative to LTO?

-cross-module-optimization (CMO) is still in an "experimental" phase. Eventually we want to enable it by default (unless a module is compiled with -enable-library-evolution).
And yes, CMO has some tradeoffs. On the one hand it can improve performance significantly, e.g. if a module calls a generic function in another module. On the other hand this can lead to a code size increase.
The concept of CMO is comparable to LTO, but it's much more powerful, because it enables the swift (SIL) optimizer to optimize across modules (and not only the LLVM optimizer).

BTW, there is a (undocumented) Xcode build setting to enable CMO: SWIFT_CROSS_MODULE_OPTIMIZATION

It would be great if you could file bugs for those crashes - or for any other CMO-related problems.

16 Likes

How do we enable the xcode build setting?

Is adding -cross-module-optimization to Target > Build Settings > Swift Compiler - Custom Flags sufficient?
Or should i also add it to my SPM targets, i.e.

targets: [
featureModules...
]
.map { (target: Target) in
target.swiftSettings = [.unsafeFlags(["-cross-module-optimization"])

}

1 Like

Just a small data point, tried this out on a small sample project and got a 4x reduction in performance (reproducible) with cross-module-optimization enabled on my M1 Max laptop:

hassila@max ~/D/G/p/swift-benchmark (main)> swift run -c release
Building for production...
Build complete! (0.08s)
Start test
1064784 usec
hassila@max ~/D/G/p/swift-benchmark (main)> swift run -Xswiftc -cross-module-optimization -c release
Building for production...
[4/4] Linking swift-benchmark
Build complete! (2.10s)
Start test
4391472 usec
hassila@max ~/D/G/p/swift-benchmark (main)> 

It is a small test project encoding simple messages with flat buffers (the flatbuffers swift code is fairly well annotated with @inline etc, was just curious to try this out and see what the difference - if any - was).

Is cross-module-optimization still something that is on the radar - worthwhile filing a report on it?f

2 Likes

Tried it again:

https://github.com/apple/swift/issues/58851

Think it's not quite ready for testing yet :slight_smile: