Closed
Description
For a call to an extern "fastcall" function on 32-bit Linux, rustc passes arguments via the stack instead of via registers, and it doesn't pop the arguments from the stack after the call. I was expecting rustc to follow the GNU/Clang "fastcall" convention (http://en.wikipedia.org/wiki/X86_calling_conventions#fastcall). Instead, it seems to be following something like the "stdcall" convention.
Test case:
cat > fastcall_callee.c << EOF
#include <stdio.h>
void callee(int x, int y) __attribute__((fastcall));
void callee(int x, int y) {
printf("%d %d\n", x, y);
}
EOF
cat > fastcall_good_caller.c << EOF
void callee(int x, int y) __attribute__((fastcall));
void good_caller() {
callee(7, 11);
callee(17, 19);
}
EOF
cat > fastcall_caller.rs << EOF
extern crate libc;
#[link(name = "fastcall_callee")]
extern "fastcall" {
fn callee(x: libc::c_int, y: libc::c_int);
}
pub fn main() {
unsafe {
callee(7, 11);
callee(17, 19);
}
}
EOF
clang -m32 fastcall_callee.c -c -O2
rm -f libfastcall_callee.a
ar rf libfastcall_callee.a fastcall_callee.o
export LD_LIBRARY_PATH=$PWD/rust-nightly-i686-unknown-linux-gnu/lib
rust-nightly-i686-unknown-linux-gnu/bin/rustc fastcall_caller.rs -L . -O
./fastcall_caller
Output:
-159272960 -7321680
0 -7323132
Segmentation fault (core dumped)
Here's the assembly output from main (with some stuff elided):
_ZN4main20h75001e02b331b561raaE:
cmpl %gs:48, %esp
ja .LBB0_2
pushl $0
pushl $12
calll __morestack
retl
.LBB0_2:
pushl %ebx
subl $8, %esp
calll .L0$pb
.L0$pb:
popl %ebx
.Ltmp3:
addl $_GLOBAL_OFFSET_TABLE_+(.Ltmp3-.L0$pb), %ebx
movl $11, 4(%esp)
movl $7, (%esp)
calll callee@PLT
subl $8, %esp
movl $19, 4(%esp)
movl $17, (%esp)
calll callee@PLT
popl %ebx
retl
I compiled both fastcall_good_caller.c
and fastcall_caller.rs
to LLVM IR, and the difference seems to be an inreg
keyword. Clang outputs it, but Rust doesn't.
fastcall_good_caller.ll:
; Function Attrs: nounwind
define void @good_caller() #0 {
tail call x86_fastcallcc void @callee(i32 inreg 7, i32 inreg 11) #2
tail call x86_fastcallcc void @callee(i32 inreg 17, i32 inreg 19) #2
ret void
}
attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }
fastcall_caller.ll:
; Function Attrs: uwtable
define internal void @_ZN4main20h75001e02b331b561raaE() unnamed_addr #1 {
entry-block:
tail call x86_fastcallcc void @callee(i32 7, i32 11)
tail call x86_fastcallcc void @callee(i32 17, i32 19)
ret void
}
attributes #1 = { uwtable "split-stack" }