Skip to content

Enable lld ICF for the Python bindings on Linux - #6535

Merged
Fedr merged 3 commits into
masterfrom
bindings-icf
Aug 7, 2026
Merged

Enable lld ICF for the Python bindings on Linux#6535
Fedr merged 3 commits into
masterfrom
bindings-icf

Conversation

@Fedr

@Fedr Fedr commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Enable lld's identical code folding when linking the Python bindings on Linux. lld is already the bindings linker there, so this is one linker flag in generate.mk's release preset. No -ffunction-sections is needed for the per-function granularity ICF requires: the bindings compile with -flto=thin, and lld's LTO code generation unconditionally emits a section per function (lld/ELF/LTO.cpp: "Always emit a section per function/datum with LTO"); the only non-LTO object in the link is the single-function libstdc++ fs-path stub.

Motivation: a symbol-level breakdown of mrmeshpy.so (from a one-off unstripped build) shows its 33 MB of code is ~193k tiny functions — mrbind registration/wrapper instantiations (~60%) and pybind11 cpp_function/caster machinery (~31%) — averaging a few hundred bytes each, many byte-identical after -Oz. ICF folds those:

mrmeshpy.so (manylinux x86_64) unpacked compressed
current 64,200 KB 18,854 KB
--icf=safe 59,056 KB (−8.0%) 17,191 KB (−8.8%)
--icf=all (this PR) 57,100 KB (−11.1%) 16,583 KB (−12.0%)

That is −2.3 MB compressed per Linux wheel and −7 MB installed, for free at link time.

Why =all rather than =safe: --icf=all may fold functions whose addresses are observably distinct, which strictly speaking breaks C++ function-pointer identity. Nothing in the bindings relies on wrapper-function pointer identity (pybind11 stores the pointers to call them, never compares them), and the full 7-distro × 2-arch wheel test matrix passed on an --icf=all build (run 31108103358, all 14 manylinux-pip-test jobs green). If reviewers prefer the conservative variant, safe is a one-word change costing 0.6 MB compressed (measured).

(The measurement runs also had explicit -ffunction-sections -fdata-sections compile flags; per the above they are no-ops for the LTO objects, so they were dropped from the final diff.)

Gated to Linux because that's where it's measured and where lld is already in use; Windows (lld-link /OPT:ICF semantics) and macOS (ld64.lld --icf) are possible follow-ups with their own measurements.

Folds byte-identical template instantiations: mrmeshpy.so -11% unpacked, -2.3 MB compressed per wheel; full distro test matrix passed on an ICF build.
@Fedr

Fedr commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Overlap with the table-driven bindings rework (mrbind#42 / #6544) re-measured on the stacked dry run https://github.com/MeshInspector/MeshLib/actions/runs/31156684018: on top of #42, ICF still folds x86_64 mrmeshpy.so 14.80 → 12.39 MB zip (−2.41 MB, −16.3%) — same absolute win as standalone. The two optimizations bite disjoint bytes (#42 removes near-duplicates ICF can't fold; ICF folds the byte-identical per-shape residue), so this PR's numbers stand regardless of merge order. Combined: 19.32 → 12.39 MB zip (−35.8%).

@Fedr
Fedr merged commit 8d1f86c into master Aug 7, 2026
50 checks passed
@Fedr
Fedr deleted the bindings-icf branch August 7, 2026 08:55
Fedr added a commit that referenced this pull request Aug 7, 2026
Enable lld's Identical Code Folding in the conservative `safe` mode for non-Debug Linux builds of the MeshLib libraries, complementing `--icf=all` for the Python bindings (#6535). `safe` folds only address-insignificant functions, so function pointers stay comparable.

Applied inside the existing lld block rather than behind a linker probe of its own: GNU ld has no ICF at all, and `safe` folding reads Clang's `.llvm_addrsig` address-significance table, which GCC does not emit -- so no non-lld row can use the option. macOS is excluded because Apple's linker already deduplicates by default, emscripten because folding there happens in Binaryen instead.

Raw shared-library size drops 2.2% on x64 (-1.35 MB) and 2.4% on arm64 (-1.57 MB), with `libMRVoxels.so` down 4.6-5.3%. The xz-compressed package moves only -0.08%: what ICF removes is duplicated code, which the compressor was already encoding as back-references, so the gain is in installed footprint rather than download size.
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