Why gcc passes char type in 8 byte format for build function

To examine the assembly, I look at the assembly generated by GCC using the -S command for some simple programs c. I have an add function that takes some int and some char and adds them together. I'm just wondering why char parameters are pushed onto the stack as 8 bytes (pushq)? Why not just push one byte?

.file "test.c" .text .globl add .type add, @function add: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) movl %esi, -8(%rbp) movl %edx, -12(%rbp) movl %ecx, -16(%rbp) movl %r8d, -20(%rbp) movl %r9d, -24(%rbp) movl 16(%rbp), %ecx movl 24(%rbp), %edx movl 32(%rbp), %eax movb %cl, -28(%rbp) movb %dl, -32(%rbp) movb %al, -36(%rbp) movl -4(%rbp), %edx movl -8(%rbp), %eax addl %eax, %edx movl -12(%rbp), %eax addl %eax, %edx movl -16(%rbp), %eax addl %eax, %edx movl -20(%rbp), %eax addl %eax, %edx movl -24(%rbp), %eax addl %eax, %edx movsbl -28(%rbp), %eax addl %eax, %edx movsbl -32(%rbp), %eax addl %eax, %edx movsbl -36(%rbp), %eax addl %edx, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size add, .-add .globl main .type main, @function main: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 pushq $9 pushq $8 pushq $7 movl $6, %r9d movl $5, %r8d movl $4, %ecx movl $3, %edx movl $2, %esi movl $1, %edi call add addq $24, %rsp leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size main, .-main .ident "GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2" .section .note.GNU-stack,"",@progbits 
 #include <stdio.h> int add(int a, int b, int c, int d, int e, int f, char g, char h, char i) { return a + b + c + d + e + f + g + h + i; } int main() { return add(1, 2, 3, 4, 5, 6, 7, 8, 9); } 
+5
source share
2 answers

When pushing values ​​on the stack, pushing should always be based on the size of the system word. If you are an old timer like me, then these are 16 bits (although I have 12-bit word-size systems!), But it really depends on the system.

Since you are talking about X86_64, you will be talking about 64-bit words. I understand that word size is usually associated with the minimum number of bytes needed to address any value in the system RAM. Since you have 64-bit memory space, 64-bit is required (or 8 bytes, a β€œfour-digit word” based on the original 16-bit word size).

+3
source

This is because it requires x86-64 SystemV ABI.

See https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-r252.pdf for a copy of the current version of the specification. See Also tag wiki for links to ABI (and more).

See page 17 of abi PDF:

Classification . The size of each argument is rounded to eight bytes. (footnote: therefore, the stack will always be aligned eight bytes).

Next (pg 16: Stack Frame ):

The end of the input argument area must be aligned at 16 (32 if __m256 is passed along the stack). In other words, the value ( %rsp + 8 ) is always a multiple of 16 (32) when control is passed to the function entry point.

If they designed it so that different different types had different widths on the stack, but 8-byte types were still always 8-byte aligned, there would be complicated rules about where the strip goes (and therefore the function finds its arguments ) depending on the types of the current and previous arguments. And that would mean that variational functions like printf would need a different calling convention that didn't collect arguments.


8-bit keystrokes are not encoded at all. Only 16-bit (with the prefix 0x66 ) or 64-bit (without the prefix or REX.W=1 ) are available. The Intel manual is a bit confused about this, implying in the text that push r32 is encoded in 64-bit mode (possibly with REX.W = 0), but it is not: See How many bytes are pushed onto the stack when I do not specify the size of the operand ? .

+9
source

All Articles