Description
I tried this code: https://rust.godbolt.org/z/j13WbYT6o
extern "Rust" {
fn bar(x: [String; 100]) -> usize;
}
pub unsafe fn foo(x: [String; 100]) -> usize {
bar(x)
}
I expected to see this happen:
Everything should be already set up on the stack appropriately to just call bar
immediately on entering foo
.
Instead, this happened:
It copies the 2400 bytes to stack, then calls bar
with the pointer to that stack copy
example::foo:
push rbx
sub rsp, 2400
mov rsi, rdi
mov rbx, rsp
mov edx, 2400
mov rdi, rbx
call qword ptr [rip + memcpy@GOTPCREL]
mov rdi, rbx
call qword ptr [rip + bar@GOTPCREL]
add rsp, 2400
pop rbx
ret
Since at the ABI level that's passed as a pointer, I suspect this is more a "how rustc emits stuff" than an LLVM bug -- maybe there's some https://llvm.org/docs/LangRef.html#parameter-attributes we could set that would help. Or maybe the unnecessary double-move in MIR is part of the problem:
fn foo(_1: [String; 100]) -> usize {
debug x => _1; // in scope 0 at /app/example.rs:5:19: 5:20
let mut _0: usize; // return place in scope 0 at /app/example.rs:5:40: 5:45
let mut _2: [std::string::String; 100]; // in scope 0 at /app/example.rs:6:9: 6:10
bb0: {
StorageLive(_2); // scope 0 at /app/example.rs:6:9: 6:10
_2 = move _1; // scope 0 at /app/example.rs:6:9: 6:10
_0 = bar(move _2) -> bb1; // scope 0 at /app/example.rs:6:5: 6:11
}
bb1: {
StorageDead(_2); // scope 0 at /app/example.rs:6:10: 6:11
return; // scope 0 at /app/example.rs:7:2: 7:2
}
}
Context
I was trying various things to see if I could remove some bad behaviour from array's map
, which is troublesome enough that it's caveated in the docs, #87609
I had thought this was due to newtypes, as in these bugs
- Using ManuallyDrop causes allocas and memcpys that LLVM cannot remove #79914
- MaybeUninit seems to prevent RVO in even the most trivial cases. #90595
But since it repro'd even without the extra wrappers I figured I'd file a distinct bug.