link-dead-code does not include symbols for unused inlined functions
Consider a file x.rs:
#[inline]
fn gggg() {}
fn main() {}
The symbol gggg is not included in the compiled binary (tested with rustc -C link-dead-code x.rs && nm x | grep gggg). If the inline attribute is removed, the symbol is included.
This is currently expected behavior as the compiler never generates any code for
#[inline]functions unless they are referenced somewhere.Michael Woerister at 2017-10-10 15:16:20
But this seems contrary to the intent of the link-dead-code (improve coverage accuracy, see https://github.com/rust-lang/rust/pull/31368)
Marco A L Barbosa at 2017-10-10 15:24:48
Yes, I can see how that would make sense when you are interested in code coverage.
@rust-lang/dev-tools @rust-lang/compiler Do we have compiler options that are targeted specifically towards code coverage? Or should we just produce code for unused inline functions if
-C link-dead-codeis specified?Michael Woerister at 2017-10-10 15:43:28
@michaelwoerister AFAIK we don't have many other options for code coverage, we're definitely long overdue for a flag for "I'd like to generate code coverage", but there's lots of hairy problems about implementing such a flag (e.g. inline functions today, generics, etc)
Alex Crichton at 2017-10-10 18:25:56
If we don't want to extend the meaning of
link-dead-code, perhaps it makes sense to add something equivalent togcc's/clang's-fkeep-inline-functionsflag?I don't feel like the smaller emit-unused-inline-functions-in-the-binary feature needs to be blocked on a more general code coverage mode.
Nick Fitzgerald at 2017-10-10 18:31:28
Yeah, adding a targeted flag instead of changing the meaning of
-C link-dead-codeseems to preferable to me.Michael Woerister at 2017-10-11 07:50:15
Triage: I'm not aware of any changes or additions here.
Steve Klabnik at 2019-05-17 13:37:31
As of rustc 1.72.0, the original repro, verbatim, doesn't reproduce the issue.
These cases still do:
rustc -C link-dead-code -C opt-level=1 x.rs && nm x | grep ggggrustc -C link-dead-code -C lto=thin x.rs && nm x | grep ggggrustc -C link-dead-code -C opt-level=1 -C lto=fat x.rs && nm x | grep gggg
While these cases do not:
rustc -C link-dead-code -C lto=fat x.rs && nm x | grep ggggrustc -C link-dead-code -C opt-level=1 -C lto=off x.rs && nm x | grep gggg
However, if the function is instead marked
#[inline(always)], the issue reproduces in all cases, including the original one.
For the
#[inline(always)]issue (without the involvement of-C opt-level> 0 or-C lto=thin), the problem is that the mono item is never placed in any CGU. The reason is in part thatMonoItem::instantiation_modereturnsLocalCopyfor the item, despite the presence of-C link-dead-code.place_mono_itemsdoesn't place it because its instantiation modeLocalCopy, and because nothing else calls it.It would be tempting to have
MonoItem::instantiation_modenot returnLocalCopyif-C link-dead-codewas used, but this would cause#[inline(always)]functions to not always be inlined, and comments like https://github.com/rust-lang/rust/pull/76896#issuecomment-758097233 make me wonder if that's a problem. On the other hand, I wonder if-C link-dead-codefundamentally relies on inlining not happening to work as desired.Tyson Nottingham at 2023-09-03 02:26:22
The right fix for this mismatch in expectations is to make
-Clink-dead-codeoverride#[inline(always)]. We clearly document that#[inline(always)]is only a hint, contrary to the wording you linked to in https://github.com/rust-lang/rust/pull/76896#issuecomment-758097233. Certainly today there are no "ABI reasons" that#[inline(always)]must actually cause the given function to be inlined, and any codebase relying on that optimization for correctness has a latent bug.Ben Kimock at 2025-01-16 01:00:21
Alternatively, both
#[inline(always)]and-Clink-dead-codehave the wrong names.#[inline(always)]should be#[inline(usually)]or something to that effect, and-Clink-dead-codeshould have been-Zhelp-bad-coverage-tools. Because that's what they are in practice.Ben Kimock at 2025-01-16 01:04:20