问题
Given the following code:
typedef struct tagRECT {
int left;
int top;
int right;
int bottom;
} RECT;
extern int Func(RECT *a, int b, char *c, int d, char e, long f, int g, int h, int i, int j);
int main() {
}
void gui() {
RECT x = {4, 5, 6, 7};
Func(&x, 1, 0, 3, 4, 5, 6, 7, 8, 9);
}
This is the assembly generated gcc x86_64 presumably on linux (I used compiler explorer).
main:
mov eax, 0
ret
gui:
push rbp
mov rbp, rsp
sub rsp, 16
; RECT x assignment
mov DWORD PTR [rbp-16], 4
mov DWORD PTR [rbp-12], 5
mov DWORD PTR [rbp-8], 6
mov DWORD PTR [rbp-4], 7
; parameters
lea rax, [rbp-16]
push 9
push 8
push 7
push 6
mov r9d, 5
mov r8d, 4
mov ecx, 3
mov edx, 0
mov esi, 1
mov rdi, rax
call Func
add rsp, 32
nop
leave
ret
It can be seen that the int
in the struct are aligned by 4 bytes. But the last 4 parameters to the function, all int
are push
d to the stack which means they were aligned by 8 bytes. Why this inconsistency?
回答1:
stack slots are 8 bytes in x86-64 calling conventions like the x86-64 System V calling convention you're using, because 32-bit push/pop is impossible, and to make it easier to keep it 16-byte aligned. See What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 (it also covers function-calling conventions, as well as system-calling conventions. Where is the x86-64 System V ABI documented?.
mov
works just fine, though, so it would have been a valid design to make 4 bytes the minimum unit for stack args. (Unlike x86-16 where SP-relative addressing modes were impossible). But unless you introduce padding rules, then you could have misaligned 8-byte args. So giving every arg at least 8-byte alignment was probably part of the motivation. (Although there are padding rules to guarantee that __m128
args have 16-byte alignment, and __m256
have 32-byte, etc. And presumably also for over-aligned structs, like struct { alignas(64) char b[256]; };
.
Only 4-byte slots would break more easily for functions without prototypes, and maybe make variadic functions more complex, but x86-64 System V already passes larger objects by value on the stack, so a stack arg may take more than one 8-byte "stack slot".
(Unlike Windows x64 which passes by hidden reference so every arg is exactly one stack slot. It even reserves 32 bytes of shadow space so a variadic function can spill its register args into the shadow space and create a full array of all the args.)
来源:https://stackoverflow.com/questions/51564190/difference-in-data-alignment-in-struct-vs-parameter