Skip to content

MIR inlining leads to LLVM error around box_free #50041

Closed
@glandium

Description

@glandium

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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions