考慮以下C99代碼:爲什麼GCC堅持非葉子被調用者將CBV結構複製到堆棧?
#include <stdio.h>
#include <stdint.h>
struct baz { uint64_t x, y; };
uint64_t foo(uint64_t a, uint64_t b, struct baz c)
{
return a + b + c.x + c.y;
}
void bar(uint64_t a, uint64_t b, struct baz c)
{
printf("%lu\n", a);
}
我期望的行爲,當gcc -O3
編譯,是c
在寄存器中傳遞到兩個foo
和bar
,在foo
使用寄存器的訪問,並在被完全忽略bar
。 GCC產生的代碼是foo
。然而,在bar
,c
從寄存器堆棧複製,然後被迅速忽略:
.file "pbv.c"
.text
.p2align 4,,15
.globl foo
.type foo, @function
foo:
.LFB22:
.cfi_startproc
leaq (%rcx,%rdx), %rdx
leaq (%rdx,%rdi), %rdi
leaq (%rdi,%rsi), %rax
ret
.cfi_endproc
.LFE22:
.size foo, .-foo
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%lu\n"
.text
.p2align 4,,15
.globl bar
.type bar, @function
bar:
.LFB23:
.cfi_startproc
movq %rdx, -24(%rsp)
movl $.LC0, %esi
movq %rdi, %rdx
xorl %eax, %eax
movl $1, %edi
movq %rcx, -16(%rsp)
jmp __printf_chk
.cfi_endproc
.LFE23:
.size bar, .-bar
.ident "GCC: (Ubuntu/Linaro 4.4.6-11ubuntu2) 4.4.6"
.section .note.GNU-stack,"",@progbits
(注意:a
和b
在%rsi
和%rdi
都過去了,c
在%rcx
和%rdx
通過。)
我可以推測的唯一原因是某種ABI要求(例如與longjmp交互)。我無法找到GCC的任何優化(-f
)選項,也無法找到抑制此行爲的GCC特定註釋。用register
註釋c
沒有幫助。
這也發生在不同的目標上。 (值得注意的是,在TileGX上,foo
在堆棧上分配了空間並釋放了空間,但沒有任何內容存儲在那裏。)我測試了GCC 4.4.6和4.6.1。
這是預期的行爲還是GCC中的錯誤?無論哪種方式,有沒有辦法解決它(旁邊使用call-by-reference或確保bar
可以是葉)?