Repository navigation
MIR inlining leads to LLVM error around box_free #50041
Description
Context: In order to experiment with an Alloc-aware Box, I was preparing to modify the box_free lang item signature, and my first step was to ensure MIR passes the right type to begin with, as noted in
rust/src/librustc_mir/transform/inline.rs
Lines 465 to 467 in b91e6a2
| // box_free takes a Box, but is defined with a *mut T, inlining | |
| // needs to generate the cast. | |
| // FIXME: we should probably just generate correct MIR in the first place... |
Legitimately, I assumed that the MIR inline code was doing the right thing, so I mimicked it in elaborate_drops.rs. The resulting code actually works fine, as far as completing the 3 stages of rustc bootstrapping is involved.
But before going the box_free route for the Alloc-aware Box, I first tried removing the special handling of Box's Drop, trying to leave it to boxed.rs, shortcutting the box_free lang item. This didn't go well, and produced a stage 1 compiler that would crash on a bad free in libsyntax's ThinVec. From which I derived a small test case that would exhibit the problem with my code. Anyways, I was going well over my head with this approach, thus switched to the box_free signature change.
So, what's the deal with this issue, will you ask? Well, it turns out that my MIR changes, essentially copied from MIR inlining, while they worked to produce an apparently working compiler, failed to compile that reduced testcase with an LLVM ERROR. I was wondering if I did something significantly different from what the MIT inlining pass was doing, so I tried to trigger it manually (since it's not enabled by default), and after some trial and error, got it to happen on a plain nightly compiler, with the following reduced testcase:
#![crate_type="lib"]
#![feature(lang_items)]
#![no_std]
#[lang = "owned_box"]
pub struct Box<T: ?Sized>(*mut T);
impl<T: ?Sized> Drop for Box<T> {
fn drop(&mut self) {
}
}
#[lang = "box_free"]
#[inline(always)]
unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
dealloc(ptr)
}
#[inline(never)]
fn dealloc<T: ?Sized>(_: *mut T) {
}
pub struct Foo<T>(T);
pub fn foo(a: Option<Box<Foo<usize>>>) -> usize {
let f = match a {
None => Foo(0),
Some(vec) => *vec,
};
f.0
}Building with rustc +nightly -Z mir_opt_level=2 test.rs yields:
Instruction does not dominate all uses!
%14 = load i64*, i64** %13, align 8
call void @_ZN4test7dealloc17hb146efc385cf7afbE(i64* %14)
LLVM ERROR: Broken function found, compilation aborted!
(Note the #[inline(always)] is only there to force MIR inlining to happen without having to go over the required threshold ; liballoc's box_free has #[inline] ; similarly, the #[inline(never)] on dealloc avoids inlining of dealloc, to limit the effects on the MIR)
Interestingly enough, --emit mir and --emit llvm-ir fail with the same error. The former outputs a truncated MIR (truncated at the entry of the first basic block), and the latter outputs nothing.
Activity
glandium commentedon Apr 18, 2018
This is what
rustc.foo.002-026.PreTrans.after.mirfrom a run with-Z dump-mir=alllooks like:Edit: and what the llvm-ir, as disassembled from
-C save-tempsoutput, looks like:[-]MIT inlining leads to LLVM error around box_free[/-][+]MIR inlining leads to LLVM error around box_free[/+]glandium commentedon Apr 18, 2018
The problem is that the code path bb1->bb9->bb21->bb20 in IR, corresponding to bb0->bb1->bb6->bb5 in MIR, is impossible, yet exists. And in that code path,
_4in MIR,%14in IR, is never initialized, and that's what LLVM doesn't like. I guess one way to look at is it to wonder whybb6in MIR has a switch in the first place.glandium commentedon Apr 18, 2018
And the answer to that last question is false edges, according to the following MIR from the mir_map pass:
glandium commentedon Apr 18, 2018
Actually, the path bb0->bb7->bb4->bb10->bb12 has
drop(_4)without_4ever being initialized, but that's pre-elaborate-drops, and I hear drop is different then, so I don't know if it bad or not.glandium commentedon Apr 18, 2018
The weird switch that eventually causes the LLVM problem is added by the elaborate-drops pass. The MIR after that phase looks like the following:
glandium commentedon Apr 18, 2018
I think the most relevant fact is this difference between MIR inlining being enabled or not:
becomes the following IR:
becomes
whiich is weird... where did the extra code go?
glandium commentedon Apr 18, 2018
The
librustc_trans/mirchanges from #48300 fix this issue.Auto merge of #50048 - glandium:issue50041, r=eddyb
Add a regression test for rust-lang#50041
Rollup merge of rust-lang#81060 - nagisa:nagisa/regression-50041, r=M…
Rollup merge of rust-lang#81060 - nagisa:nagisa/regression-50041, r=M…
Auto merge of rust-lang#81089 - m-ou-se:rollup-z7iac6i, r=m-ou-se