Description
After llvm/llvm-project@6c8adc5, inlining in cross-language LTO happens in cases where it didn't happen before, including cases where things go very bad (more details in https://bugzilla.mozilla.org/show_bug.cgi?id=1789779#c7)
It seems to boil down to LLVM not liking that rust defines its extern "C"
functions in significantly different ways than clang does for the C/C++ code that calls it. For example:
// rustc --emit=llvm-ir foo.rs --crate-type=lib --target=i686-unknown-linux-gnu
#[repr(C)]
pub struct Foo(pub u32);
#[no_mangle]
pub unsafe extern "C" fn foo(foo: Foo) -> u32 {
foo.0
}
The caller:
// clang -o - -S foo.c -emit-llvm --target=i686-unknown-linux-gnu
struct Foo { int a; };
extern int foo(struct Foo f);
int var(struct Foo f) {
return foo(f);
}
Rust defines the function as:
Foo = type { i32 }
; Function Attrs: nonlazybind uwtable
define i32 @foo(%Foo* byval(%Foo) %foo) unnamed_addr #0 {
while the caller C code does this:
%struct.Foo = type { i32 }
(...)
declare i32 @foo(i32) #1
The equivalent C code:
// clang -o - -S foo.c -emit-llvm --target=i686-unknown-linux-gnu
struct Foo { int a; };
int foo(Foo foo) {
return foo.a;
}
defines the function as:
%struct.Foo = type { i32 }
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @foo(i32 %0) #0 {
(Ironically, rustc transforms a non-extern "C"
version of the function to the same declaration as clang's)
Arguably, there's an underlying LLVM bug not being able to handle this case, which /could/ be considered fine, but I'm not sure it's supposed to be.
It's worth noting that rustc does not use a byval for e.g. x86_64-unknown-linux-gnu.
Cc: @nikic