Change:
<ins> // revisions: cfail1 cfail2 // build-pass // rust-lang/rust#69798: // // This is analgous to cgu_invalidated_when_import_added, but it covers a // problem uncovered where a change to the *export* set caused a link failure // when reusing post-LTO optimized object code. pub struct Foo {} impl Drop for Foo { fn drop(&mut self) { println!("Dropping Foo"); } } #[no_mangle] pub extern "C" fn run() { thread_local! { pub static FOO : Foo = Foo { } ; } #[cfg(cfail2)] { FOO.with(|_f| ()) } } pub fn main() { run() } </ins>

Discussion:
From nightly-2020-02-28 (nightly-2020-02-27 was fine), I get link errors if I compile a cdylib with a thread local that isn't used, then compile again after referencing the thread local. To reproduce, create a.rs: Then run: Uncomment the commented line and rerun the above command. This produces the following link error: It's possible however that the underlying bug is actually older though, since at opt-level=1, I get link errors from 1.32.0 onwards (1.31.0 was fine). In that case, the Drop implementation isn't needed. Linking fails with: The exact symbol that's reported varies. Versions: rustc 1.43.0-nightly ( 2020-03-06) cc (Debian 9.2.1-16) 9.2.1 GNU ld (GNU Binutils for Debian) 2.33.1 <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
claim
Here's what appears to be happening: generate a CGU containing some drop glue, along with some globals (e.g. the "Dropping Foo" constant). When LTO runs, LLVM consults the ThinLTO index, and . This allows it to . This allows said constant (e.g. ) to be deleted entirely, since it cannot be accessed outside of the CGU. uncomment . Now, we end up needing to import this drop glue (since it gets called by the drop glue for ). We re-generate the module performing the import, but not the module we are importing from. freshly codegenned module ends up inlining a function from the re-used module, which references an constant from the re-used module. However, the post-optimization IR/ASM for the re-used module no longer contains this constant, since it was unused at the time the re-used module was optimized. Note that exact names imported seem to be extremely sensitive to local changes, so you might get different constants/containing functions.

The general problem seems to be that LLVM looks 'backward' in the dependency graph during ThinLTO - that is, it looks at whether any modules import from the current module, not just what modules the current modules imports. I think there are a few possible solutions: a module goes from exporting nothing to exporting something, rebuild any modules which now import it. This is the most targeted fix, but I'm worried that it could be quite fragile. As far as I can tell, LLVM assumes that post-LTO files are never reused. Theoretically, LLVM could perform a more precise check to see which functions are actually 'exported' (e.g. used elsewhere) from a given ThinLTO CGU. the check in to rebuild both the 'importer' and 'importee' when the import map changes. That is, if module 'A' starts/stops importing from module 'B', we rebuild both A and B. This is a broader change than 1), and should hopefully continue to work even if LLVM gets 'smarter'. However, it could cause us to unnecessarily re-run codegen for some modules, which could have a significant performance impact. LLVM to be more conservative about deciding that a module has no exports. It's not clear to me how easy such a change would be, or whether upstream would accept it. Someone who's more familiar with LLVM should weigh in here.
ping icebreakers-llvm
Hey LLVM ICE-breakers! This bug has been identified as a good "LLVM ICE-breaking candidate". In case it's useful, here are some [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 [instructions]: https://rust- cc
We have the first non working nightly which is , although it would be nice to get the first regresed commit or PR using .
Tagging as P-high until we know how bad this issue is.
From , and without really knowing what's going on, seems likely to me. It mentions removing MIR Drop terminators that will never be reached.
Seems my guess was wrong. I ran cargo-bisect-rustc (neat command BTW). It reported the regression with commit
For reference, is part of PR ; i.e. the latter PR either injected this bug, or tickled a latent bug in LLVM.

(Which of the latter two cases we are in depends in part on how we resolve the by .)
Of the options outlined by , I think number 2 is best: I have started prototyping it on my laptop, since I am familiar with the code I in PR
My prototype is now done and appears to fix this case. I have not attempted to benchmark yet; I figure we can land it, or at least run it through , and see where it goes.
marking as stable-to-beta regression. The underlying bug here is old, but the change that exposes the bug here (PR ) has not yet made it to stable.