Build: enable /OPT:REF for MSVC RelWithDebInfo - #1833
Conversation
MSVC turns /OPT:REF and /OPT:ICF off whenever /DEBUG is passed, and CMake's default RelWithDebInfo link flags are "/debug /INCREMENTAL". The result is that RelWithDebInfo keeps every unreferenced COMDAT that made it into the static libraries, even though it is an optimized configuration. Turning incremental linking off and /OPT:REF on shrinks the Playground: Playground.exe RelWithDebInfo 11,212,800 -> 4,562,432 bytes (-59.3%) /OPT:ICF is deliberately left off: it measured zero additional bytes here, and folding identical functions makes stack traces ambiguous, which is the opposite of what RelWithDebInfo is for. Debug is untouched. Release and MinSizeRel already get /OPT:REF implicitly because they do not link with /DEBUG. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Adjust MSVC RelWithDebInfo linker behavior so unreferenced code is stripped (without enabling ICF), reducing binary size while keeping /DEBUG and PDB generation.
Changes:
- Overrides MSVC RelWithDebInfo linker flags to force
/INCREMENTAL:NOand/OPT:REF. - Removes any existing incremental/OPT:REF settings from the three RelWithDebInfo linker flag variables before appending the desired flags.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Replaces the string surgery on CMAKE_*_LINKER_FLAGS_RELWITHDEBINFO, which could corrupt a pre-existing /INCREMENTAL:YES by removing only the /INCREMENTAL prefix and leaving a dangling :YES. Verified through the generated Playground.vcxproj that CMake still folds these into the structured properties -- OptimizeReferences=true and LinkIncremental=false on RelWithDebInfo only, with nothing leaking into AdditionalOptions -- and that the size win is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 88569c10-a7ff-4373-9a58-afa9c68b8c09
ryantrem
left a comment
There was a problem hiding this comment.
Does this slow down the builds then, with incremental linking disabled?
|
Good question — I measured it rather than guessing, and the short answer is: a little, but you don't actually get a choice, and clean builds are unaffected. Incremental relink of
So about +0.8s per edit-relink cycle, on a link that also drops the binary from 11,060,224 to 4,407,296 bytes. The important part: and the resulting binary was byte-for-byte the same size (4,407,296). So the flag only silences LNK4075 on every link of every target — it costs nothing extra. The real trade is Clean builds and CI are unaffected: they link once either way, and compilation dominates. If the edit-relink cost is unwelcome for local iteration, two easy outs — happy to do either if you'd prefer:
My preference is to leave it as is: 0.8s on an incremental relink seems a fair price for a 60% smaller binary, and RelWithDebInfo is the config where the bloat actually matters. |
RelWithDebInfo is an optimized configuration, but on MSVC it currently keeps every unreferenced function that made it into the static libraries.
The reason is that MSVC turns
/OPT:REFand/OPT:ICFoff whenever/DEBUGis passed, and CMake's default RelWithDebInfo link flags are/debug /INCREMENTAL. So nothing gets stripped.Turning incremental linking off and
/OPT:REFon fixes that:Notes
/OPT:ICFis deliberately left off. It measured zero additional bytes on top of/OPT:REF, and folding identical functions makes stack traces ambiguous — the opposite of what RelWithDebInfo is for./INCREMENTAL:NOis required, not incidental: incremental linking is incompatible with/OPT:REF./OPT:REFimplicitly because they do not link with/DEBUG./DEBUGstays on and the PDB is still produced.Verification
Confirmed through the generated
Playground.vcxprojthatOptimizeReferencesistruefor RelWithDebInfo only, and thatEnableCOMDATFoldingstays false in every configuration.The stripped binary was then run through the full visual test suite headless, to confirm nothing that is actually reachable got removed:
The flags are applied with
add_link_options()and a$<$<CONFIG:RelWithDebInfo>:...>generator expression, so CMake owns the tokenization and folds them into the structured project properties rather than rawAdditionalOptionstext:OptimizeReferencesLinkIncremental