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
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.