Repository navigation
Drop implementations not being called on panic with Fat LTO. #64655
Description
Hi,
It looks like calls to Drop::drop() are getting removed when using lto = "fat" for a release build. It doesn't seem to occur with lto = "thin" or lto = "no".
I've got a test case here: https://github.com/cstorey/fat-lto-drop-repro, which can be reproduced like this:
: cez@ceri-no-mini; cargo --version; rustc --version; cargo run; cargo run --release
cargo 1.37.0 (9edd08916 2019-08-02)
rustc 1.37.0 (eae3437df 2019-08-13)
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/lto-drop`
About to panic
thread 'main' panicked at '???', src/libcore/option.rs:1034:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Dropping a Droppable
Finished release [optimized] target(s) in 0.00s
Running `target/release/lto-drop`
About to panic
thread 'main' panicked at '???', src/libcore/option.rs:1034:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
: cez@ceri-no-mini;
(more details in the transcript)
For context, I first noticed this in a project where I am using an mpsc::channel to communciate between threads, where a producer thread reads messages from the network, and the main thread saves those to a database.
I was relying on the fact that the Drop implementation for a channel will in effect close the channel, so the main thread can find out that the producer has failed, and so we should kill the process. What I saw instead was the producer thread panicing, but the main thread would never get woken up.
I've seen this on macOS Darwin 17.7.0 x86_64 and Linux 5.0.0-25-generic #26-Ubuntu SMP Thu Aug 1 12:04:58 UTC 2019 x86_64.
Activity
pnkfelix commentedon Sep 26, 2019
triage: P-high. Assigning to self. Removing nomination label.
pnkfelix commentedon Sep 30, 2019
Doing some bisection. This was introduced between Rust 1.32 and 1.33
pnkfelix commentedon Sep 30, 2019
More specifically, this was introduced between nightly-2018-12-08 and nightly-2018-12-14:
(unfortunately no nightlies are available between those two points in the rustup archive.)
pnkfelix commentedon Sep 30, 2019
These are the PR's that bors merged between those two points:
pnkfelix commentedon Sep 30, 2019
According to my bisection via local builds, this was injected by #55982 (!)
pnkfelix commentedon Sep 30, 2019
(Have I mentioned before that we probably need to be doing more testing of the various LTO configurations? Would it be feasible to run our test suite in all of the lto modes from {
lto=no,lto=thin,lto=fat} ?)pnkfelix commentedon Oct 1, 2019
For what its worth, @RalfJung 's PR #63909 fixes this
noncrab commentedon Oct 1, 2019
Thanks for tracking this down @pnkfelix!
pnkfelix commentedon Oct 2, 2019
Okay I added some instrumentation to the compiler and I think I know where the problem here is coming from.
Here's the executive summary:
externfunctions to abort by default on panic #55982 changed the logic for where we add the LLVM attributeno_unwindI myself was confused by this comment, because it looked to me like the PR as written was taking pains to return
unwind=truefor abis that matchAbi::RustandAbi::RustCall.So why is the test crate in this issue illustrating the problem outlined by @RalfJung above?
First of all, here is a standalone main.rs that reproduces the problem for me. (You still have to turn on
LTO=fatto observe the issue, but no other optimizations; e.g.-C opt-level=0 -C lto=fatstill reproduces the issue for me):Running the compiler with
-C lto=fat -C save-tempsgave me intermediate.bcfiles that I could disassemble viallvm-disand then inspect the resulting.llfiles. This led me to notice that some functions hadnounwindon them that I did not expect.Second, I instrumented a local
rustcto report (viaprintln!) whenever it used either of these two cases:rust/src/librustc_codegen_llvm/attributes.rs
Lines 278 to 287 in f2023ac
Just imagine the above, with two inserted
println!calls that look like:and
And then I reviewed the output this spits out during both the stage1 bootstrapping and the actual compilation of the test case above.
Here's the smoking gun from that output, I think:
fmt::Debugimpl forFnSigdoes not include the abi (nor the unsafety). I'm guessing we'd see "Rust" there if that were included.namely, we are going through that first branch, already mentioned by @RalfJung , when we compile this declaration:
rust/src/libcore/panicking.rs
Lines 73 to 77 in f2023ac
This causes the generated declaration for
panic_implto be taggednounwind, even though that function is pretty much the function that is going to unwind.(But you need to have a somewhat nasty combination of factors to actually observe the optimization performed by LLVM in this case, which is how I guess this has gone relatively unobserved for so long, apart from what @RalfJung noticed in their own work.)
pnkfelix commentedon Oct 2, 2019
The short version of my previous comment is that I bet I could make a small change to mark extern fn's with Rust or RustCall ABIs as unwinding, which would have less impact than @RalfJung's proposal and thus be a better candidate for backport.
Rollup merge of rust-lang#65020 - pnkfelix:targetted-fix-for-always-m…
Rollup merge of rust-lang#65020 - pnkfelix:targetted-fix-for-always-m…
Rollup merge of rust-lang#65020 - pnkfelix:targetted-fix-for-always-m…
Auto merge of rust-lang#65020 - pnkfelix:targetted-fix-for-always-mar…