Code change:
Hello, Linking crate on fails when using . The link succeeds when using I expected to see this happen: linking successful. Instead, this happened: :
I'm adding a note here because my brain gets confused by github's bold-text "mentioned this issue" notes. I see the "No counters provided the source-hash" and jump to the wrong conclusion that this is related. (I think I've done this more than once.) So, NOTE TO SELF, this issue is NOT related to the "No counters provided the source hash" bug... Thanks and sorry for the churn...
Even worth, I suspect you deleted your previous comment :yum:. In my mail box I got from github on your behalf, I have the message "This should be fixed. There was a brief window in the nightly builds (less than a week I think) where this was a known bug. It was resolved."), but I have no trace of it in the conversation, so I am even more lost with just above message. If you wish you can edit messages instead of deleting. Users can see that your message was edited and they can access edition history to see what changed. But considering all the effort you are giving with instrument coverage, and how important this is to Rust (in my humble opinion), you are fully forgiven :smile:
I see is about to be stabilized: Will this issue be solved before stabilization?
I rarely have a useable windows configuration for debugging coverage. Any chance you can look into this? Also note the theory and workaround suggested in I think stabilizing the flag does not imply every edge case is understood and resolved so it may not block stabilization, but I haven't had any time to look into this one since it was filed. I suspect it has to do with generating function symbols for unused functions in the covmap generation code but not sure if something else is involved (macros were mentioned in the linked comment). Since I'm out for 3 weeks I'm not going to have a chance to look at this for at least that long. Seems very rare too. Thanks! Cc:
Yep, I'll take a look this week!
I hit this with the crate today as well, so any project with in its tree ( for me) may hit it. That said, only three (known) cases isn't a lot of evidence towards rarity or against it...
- Did you have any opportunity to look into this?
I'm still looking into this but I have managed to construct a minimal (I believe) repro: Which, with the appropriate flags, will then trigger the link error: There's quite a lot of dead code in the "minimal" repro but removing any of it or even moving it around causes the error to go away. We need basically this exact structure to get the codegen units to be placed into the state which triggers this bug.
Thank you! Having a reproducible example like this is still very helpful. It's not a stretch to assume the trigger relates to the CGU-specific logic in My educated guess (still a guess though) is that we're trying to for the same unused from multiple CGUs. I will try your example on Linux as well, just to be certain this reproducible case really is Windows specific. If it is the CGU logic, and is Windows-specific, the question I have is, what's different between Windows and other platforms like Linux? Some half-baked theories: Codegen on Linux is not actually using multiple codegen units? (Or not as commonly/extensively as on Windows?) I assume this is not the case. Some subtle difference in the parallelism between Windows and Linux is making this situation more likely on Windows? I can't picture how it would be that deterministic on Windows. Differences between the linkers and/or linker parameters, and how they handle duplicate symbols. If so, is this a "bug" in that we can "fix" to make linking on Windows more like linking on the other platforms?
It might also be worth logging the s in that call on line 374, maybe along with identifying the CGU, to confirm that more than one CGU is trying to define the same (unless you already know this to be the case).
- What is the filename for the last chunk of rust code? It doesn't appear to be referenced from any of the other files, unless I'm missing something. I wonder why it gets pulled into the compilation?
Woops, sorry about that! I've updated the comment to include the file name. It's pulled in by the statement in . Codegen on Linux does use multiple cgus (especially when is passed). At least from my tests, is the minimum number of CGUs required to trigger this. I haven't had any issue reproducing with this source (ie, it's not intermittent when the source code is not changed). So it doesn't feel like a parallelism/determinism issue to me. This seems like the most likely issue to me. I'm digging into the symbol table and LLVM IR ...
FYI, I just compiled this on my Linux machine, using , and I a debug message. It does indeed appear to define the same from different CGUs, but it links successfully. This log is printed twice, with two different cgu names:
(In case you'd like to try the same, I this line right before the call to :
Here's another (surprising? to me anyway) clue: I a debug log here: And two CGU's ( shown below and a different CGU) printed identical results: Doesn't this mean that the two CGUs are redundantly generating code for the same 5 functions (6 including the undefined function coverage is adding)? Is there something about the symbols generated for the "used" functions shown here that makes them unique within their CGU, so Windows is not getting linker errors on the used functions as well? Wishful thinking, maybe this identifies an opportunity to improve compiler performance, but more likely I just misunderstood the relationship between the CGUs and the results from the call to .
Ok, that was super helpful and pointed me in the direction of the actual minimal repro. Thanks Since is marked , it's included in the CGU for and the CGU for . Therefore, both CGUs think they should be responsible for generating the unused function and there are two copies of it generated. While the functions are generated as weak symbols, the MSVC linker seems to treat weak symbols differently than unix-y linkers do. I'm a bit curious why we use instead of for these functions? Using seems to produce the correct results (at least in this simple test): Changing to use those attributes instead seems like a quick, easy fix to resolve this for now. A better solution might be to reserve one CGU and put all of the dead code in that CGU which would sidestep the entire issue by ensuring there is only one CGU with these functions in it.
Let's try it! Are you planning to submit the PR or do you want me to. I think this is harder than it might seem on the surface. If I recall correctly, this was something I looked into, but the information required was not where I needed it, after CGU partitioning. Someone may know a way to do it, but I just want to caution that it might not be that trivial.
Let's see if CI passes
Historical context: See ... My depth of understanding of all of the different linkage options is still more shallow than I'd like. I read the LLVM documentation descriptions several times, but the number of combinations, and their effects, are still hard for me to understand. Some choices I had to make were an "educated guess" on my part. As you can see, MOST of the time, the current linkage works, but if your suggested changes resolve these issues without breaking anything else, that's fantastic.
Ah, thanks! I won't claim my understanding of the linkage options is complete either, but I think will cause the dead code functions' symbols to be made private to the CGU they're declared in and prevent any name collisions by renaming if necessary.

Related discussion:
text: <ins> ../coverage/issue-85461.rs: 1|       |// Regression test for #85461: MSVC sometimes fail to link with dead code and #[inline(always)] 2|       | 3|       |extern crate inline_always_with_dead_code; 4|       | 5|       |use inline_always_with_dead_code::{bar, baz}; 6|       | 7|      1|fn main() { 8|      1|    bar::call_me(); 9|      1|    baz::call_me(); 10|      1|} ../coverage/lib/inline_always_with_dead_code.rs: 1|       |// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 -Copt-level=0 2|       | 3|       |#![allow(dead_code)] 4|       | 5|       |mod foo { 6|       |    #[inline(always)] 7|      2|    pub fn called() { } 8|       | 9|      0|    fn uncalled() { } 10|       |} 11|       | 12|       |pub mod bar { 13|      1|    pub fn call_me() { 14|      1|        super::foo::called(); 15|      1|    } 16|       |} 17|       | 18|       |pub mod baz { 19|      1|    pub fn call_me() { 20|      1|        super::foo::called(); 21|      1|    } 22|       |} </ins>