Skip to content

module-template: fix the IPO check, and default IPO off - #11

Merged
siahisaforker merged 1 commit into
ExpansionPak:moderngekko-vendorfrom
dougchansan:module-template-ipo-check
Aug 10, 2026
Merged

module-template: fix the IPO check, and default IPO off#11
siahisaforker merged 1 commit into
ExpansionPak:moderngekko-vendorfrom
dougchansan:module-template-ipo-check

Conversation

@dougchansan

Copy link
Copy Markdown

The IPO support check could never pass on macOS, and the failure was silent.

moderngekko-port configures with CMAKE_NINJA_FORCE_RESPONSE_FILE=1, which the module genuinely needs — 186 objects do not fit on a command line. check_ipo_supported's try-compile inherits the environment, so its static-library step runs ar qc libfoo.a @foo.rsp, and Apple's /usr/bin/ar has no @file support:

ar: @CMakeFiles/foo.rsp: No such file or directory

The check failed, CMake logged one -- Module IPO disabled: line into a build log nobody reads, and the build carried on without -flto=thin — while manifest.txt went on recording flags=compile:-O2 -flto=thin ... link:-flto=thin. Object files were plain Mach-O, not LLVM bitcode.

No module built on macOS has ever had cross-translation-unit inlining. That is worth knowing before concluding anything about why a runtime helper did not get inlined into generated code — it cost me a build and a benchmark to find out.

The fix scopes the variable off around the check only and restores it before the generator reads it, so the real link keeps its response files.

Why the default also flips to OFF

With the check repaired, LTO was measured and did not pay. Mario Kart: Double Dash on arm64, same PGO profile, only -flto=thin differing, alternating A/B with a reversed final block on one savestate:

no LTO   n=5  mean 1.1826  [1.1729-1.1959]
LTO      n=5  mean 1.1733  [1.1387-1.1879]
-0.8%, ranges overlap -- unproven

The host was not quiet (8 of 10 runs had a competitor above 70% of a core), so read that as "no win", not "a loss". Pushing harder did not help either: -Wl,-mllvm,-import-instr-limit=500 -Wl,-mllvm,-inline-threshold=600 does inline the hot FP helper — ppc_fmuls drops from 5846 call sites to 861 — and measured -1.7% while growing the module by 7 MB.

LTO also makes the link markedly slower, so OFF-by-default costs nothing measurable and saves build time. -DRECOMPCORE_MODULE_ENABLE_IPO=ON still works for anyone wanting to re-measure.

One caveat on that default

On Linux, GNU ar does support @file, so the check presumably passed there and those builds have had thin-LTO all along. This default takes it away from them, and I have no Linux measurement. The two halves are independent — if you would rather not change Linux behaviour on the strength of an arm64-only number, take the check fix and drop the default change.

Verified

  • default: no -flto in FLAGS, no IPO-disabled message
  • -DRECOMPCORE_MODULE_ENABLE_IPO=ON: -flto=thin in FLAGS, response files still forced (RSP_FILE in rules.ninja), objects report LLVM bitcode

The IPO support check could never pass on macOS, and the failure was silent.

moderngekko-port configures with CMAKE_NINJA_FORCE_RESPONSE_FILE=1, which the
module genuinely needs -- 186 objects do not fit on a command line.
check_ipo_supported's try-compile inherits the environment, so its static
library step runs `ar qc libfoo.a @foo.rsp`, and Apple's /usr/bin/ar has no
@file support:

    ar: @CMakeFiles/foo.rsp: No such file or directory

The check therefore failed, CMake logged one "Module IPO disabled:" line, and
the build carried on without -flto=thin -- while manifest.txt went on recording
`flags=compile:-O2 -flto=thin ... link:-flto=thin`. Object files were plain
Mach-O rather than LLVM bitcode. No module built on macOS has ever had
cross-translation-unit inlining, which is worth knowing before drawing any
conclusion about why a runtime helper did not get inlined into generated code.

Scope the variable off around the check only and restore it before the
generator reads it, so the real link keeps its response files.

Also flip RECOMPCORE_MODULE_ENABLE_IPO to OFF, because with the check repaired
LTO was measured and did not pay. Mario Kart: Double Dash on arm64, same PGO
profile, only -flto=thin differing, alternating A/B with a reversed final
block on one savestate:

    no LTO   n=5  mean 1.1826  [1.1729-1.1959]
    LTO      n=5  mean 1.1733  [1.1387-1.1879]
    -0.8%, ranges overlap -- unproven

The host was not quiet, so this is "no win" rather than "a loss". Pushing
further -- -Wl,-mllvm,-import-instr-limit=500 -Wl,-mllvm,-inline-threshold=600,
which does inline the hot FP helper, taking ppc_fmuls from 5846 call sites to
861 -- measured -1.7% and grew the module by 7 MB.

One caveat on the default. On Linux, GNU ar does support @file, so the check
presumably passed there and those builds have had thin-LTO all along; OFF takes
it away from them. That is not backed by a Linux measurement. If you would
rather not change Linux, take the check fix and drop the default change --
they are independent, and the option can still be set per build.
@siahisaforker

Copy link
Copy Markdown

I could not push the maintainer fix to this head branch because it is in a sibling fork of RecompCore. The fixed replacement is #13; merge #13 instead of this PR.

@siahisaforker
siahisaforker merged commit d490451 into ExpansionPak:moderngekko-vendor Aug 10, 2026
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.

2 participants