-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[code coverage] Fix missing dead code in modules that are never called #92142
[code coverage] Fix missing dead code in modules that are never called #92142
Conversation
The issue here is that the logic used to determine which CGU to put the dead function stubs in doesn't handle cases where a module is never assigned to a CGU. The partitioning logic also caused issues in rust-lang#85461 where inline functions were duplicated into multiple CGUs resulting in duplicate symbols. This commit fixes the issue by removing the complex logic used to assign dead code stubs to CGUs and replaces it with a much simplier model: we pick one CGU to hold all the dead code stubs. We pick a CGU which has exported items which increases the likelihood the linker won't throw away our dead functions and we pick the smallest to minimize the impact on compilation times for crates with very large CGUs. Fixes rust-lang#86177 Fixes rust-lang#85718 Fixes rust-lang#79622
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks really nice. Thank you!
It's hard to generate an extensive test to validate this works as you've explained, but I trust you are right. But assuming it does work, I'm very excited about this improvement!
I don't catch any code issues or have questions about the implementation, but I have a couple of comments and a nit.
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt
Outdated
Show resolved
Hide resolved
Side note: while working on this PR, I've started to wonder if we should really be stabilizing these options in #90132:
My perception is that we've added these options to allow users to work around issues caused by our attempts to generate coverage data for dead functions and uninstantiated generic functions. Do you (@tmandry and @richkadel) think these options are useful to users for any other reasons? If not, I'll make sure we update #90132 to only stabilize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the simplicity. Generating these stubs is hopefully going to be fast, so hopefully putting them on one CGU doesn't cause performance issues. Though it would be if we could measure this somehow.
I don't recall why we added those options. The only reason I think we might want to keep them would be build performance. |
I would support that change. I think I agree with you. |
9fde2a3
to
dbc33b5
Compare
Thanks! Reviews addressed |
|
||
let dead_code_cgu = cgus_with_exported_symbols.last_mut().unwrap(); | ||
.last() | ||
.unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible we'd have no CGUs with externally linked symbols?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, a CGU compiles an LLVM Module.
Logically (but maybe naively), I assume a Module without any externally linked symbols could not be incorporated into a program that you could then link and run. And if that's the case, there would be no way to run a binary, using that Module, in order to generate coverage profraw data.
So even if we had a Module with no externally linked symbols, the only way to add coverage mapping data (and unused function placeholders) would be to add them to a Module that is not being compiled (which is, at least, impractical).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, @richkadel is correct. There are basically two cases: we are compiling an executable or we are compiling a library. For executables, the entry point is externally visible/linked. For libraries, something has to be externally linked or the library isn't useable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For libraries, something has to be externally linked or the library isn't useable.
Ah, so this isn't quite true. If a library contains only macros, then it will have one CGU which is empty. In this case, I'll just fall back to the first CGU and also add an assert to verify there is at least one CGU.
dbc33b5
to
ebc0d0d
Compare
Thanks for this!! @bors r+ |
📌 Commit ebc0d0d has been approved by |
…ioning, r=tmandry [code coverage] Fix missing dead code in modules that are never called The issue here is that the logic used to determine which CGU to put the dead function stubs in doesn't handle cases where a module is never assigned to a CGU (which is what happens when all of the code in the module is dead). The partitioning logic also caused issues in rust-lang#85461 where inline functions were duplicated into multiple CGUs resulting in duplicate symbols. This commit fixes the issue by removing the complex logic used to assign dead code stubs to CGUs and replaces it with a much simpler model: we pick one CGU to hold all the dead code stubs. We pick a CGU which has exported items which increases the likelihood the linker won't throw away our dead functions and we pick the smallest to minimize the impact on compilation times for crates with very large CGUs. Fixes rust-lang#91661 Fixes rust-lang#86177 Fixes rust-lang#85718 Fixes rust-lang#79622 r? `@tmandry` cc `@richkadel` This PR is not urgent so please don't let it interrupt your holidays! 🎄 🎁
@bors r- |
@richkadel do you have any suggestions for troubleshooting the failure? I've tried compiling the test case manually and running the coverage tools but the output seems to match the expected test results. I haven't had any luck getting the |
b45a3f2
to
e014dba
Compare
e014dba
to
15a5a19
Compare
As discussed in rust-lang#92142 (comment), tests that contain multiple files order their results differently on Windows and Linux which the test runner currently can't handle correctly. For now, ignore the "bin" part of the test and only include the unused library dependency which is what the test really cares about anyway.
15a5a19
to
e9cac4c
Compare
Yep, that's exactly it. Thanks for looking at this @richkadel! I've added the entry point file for that test to the ignored files list and it resolves the issue. This should be ready to go. |
@tmandry - PTAL ... Should be good to resubmit to bors queue. |
@bors r+ |
📌 Commit e9cac4c has been approved by |
…ioning, r=tmandry [code coverage] Fix missing dead code in modules that are never called The issue here is that the logic used to determine which CGU to put the dead function stubs in doesn't handle cases where a module is never assigned to a CGU (which is what happens when all of the code in the module is dead). The partitioning logic also caused issues in rust-lang#85461 where inline functions were duplicated into multiple CGUs resulting in duplicate symbols. This commit fixes the issue by removing the complex logic used to assign dead code stubs to CGUs and replaces it with a much simpler model: we pick one CGU to hold all the dead code stubs. We pick a CGU which has exported items which increases the likelihood the linker won't throw away our dead functions and we pick the smallest to minimize the impact on compilation times for crates with very large CGUs. Fixes rust-lang#91661 Fixes rust-lang#86177 Fixes rust-lang#85718 Fixes rust-lang#79622 r? `@tmandry` cc `@richkadel` This PR is not urgent so please don't let it interrupt your holidays! 🎄 🎁
…ioning, r=tmandry [code coverage] Fix missing dead code in modules that are never called The issue here is that the logic used to determine which CGU to put the dead function stubs in doesn't handle cases where a module is never assigned to a CGU (which is what happens when all of the code in the module is dead). The partitioning logic also caused issues in rust-lang#85461 where inline functions were duplicated into multiple CGUs resulting in duplicate symbols. This commit fixes the issue by removing the complex logic used to assign dead code stubs to CGUs and replaces it with a much simpler model: we pick one CGU to hold all the dead code stubs. We pick a CGU which has exported items which increases the likelihood the linker won't throw away our dead functions and we pick the smallest to minimize the impact on compilation times for crates with very large CGUs. Fixes rust-lang#91661 Fixes rust-lang#86177 Fixes rust-lang#85718 Fixes rust-lang#79622 r? ``@tmandry`` cc ``@richkadel`` This PR is not urgent so please don't let it interrupt your holidays! 🎄 🎁
…askrgr Rollup of 9 pull requests Successful merges: - rust-lang#90001 (Make rlib metadata strip works with MIPSr6 architecture) - rust-lang#91687 (rustdoc: do not emit tuple variant fields if none are documented) - rust-lang#91938 (Add `std::error::Report` type) - rust-lang#92006 (Welcome opaque types into the fold) - rust-lang#92142 ([code coverage] Fix missing dead code in modules that are never called) - rust-lang#92277 (rustc_metadata: Stop passing `CrateMetadataRef` by reference (step 1)) - rust-lang#92334 (rustdoc: Preserve rendering of macro_rules matchers when possible) - rust-lang#92807 (Update cargo) - rust-lang#92832 (Update RELEASES for 1.58.) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
The comment says "Find the smallest CGU that has exported symbols and put the dead function stubs in that CGU". But the code sorts the CGUs by size (smallest first) and then searches them in reverse order, which means it will find the *largest* CGU that has exported symbols. The erroneous code was introduced in rust-lang#92142. This commit changes it to use a simpler search, avoiding the sort, and fixes the bug in the process.
The issue here is that the logic used to determine which CGU to put the dead function stubs in doesn't handle cases where a module is never assigned to a CGU (which is what happens when all of the code in the module is dead).
The partitioning logic also caused issues in #85461 where inline functions were duplicated into multiple CGUs resulting in duplicate symbols.
This commit fixes the issue by removing the complex logic used to assign dead code stubs to CGUs and replaces it with a much simpler model: we pick one CGU to hold all the dead code stubs. We pick a CGU which has exported items which increases the likelihood the linker won't throw away our dead functions and we pick the smallest to minimize the impact on compilation times for crates with very large CGUs.
Fixes #91661
Fixes #86177
Fixes #85718
Fixes #79622
r? @tmandry
cc @richkadel
This PR is not urgent so please don't let it interrupt your holidays! 🎄 🎁