考虑以下C99代码:
#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
,使用{{1}中的寄存器进行访问在foo
中完全忽略了}。 GCC生成代码,为bar
执行此操作。但是,在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
}}。)
我可以推测的唯一原因是某种ABI要求(例如与longjmp的交互)。我找不到GCC的任何优化(%rdx
)选项,也没有找到抑制此行为的GCC特定注释。使用-f
注释c
无济于事。
这也适用于不同的目标。 (值得注意的是,在TileGX上,register
在堆栈上分配和释放了空间,但没有任何内容存储在那里。)我测试了GCC 4.4.6和4.6.1。
这是预期的行为还是GCC中的错误?无论哪种方式,是否有某种方法可以解决它(除了使用call-by-reference或确保foo
可以是叶子)?
答案 0 :(得分:0)
这个缺点与bug 44194中提到的相同,其补丁出现在最新版本的GCC(4.7.2)中。
原因大致是对printf
(或任何函数)的调用被认为能够访问内存中的任何内容,包括基于堆栈的本地。该补丁会导致被调用者无法认为基于堆栈的本地人可以访问。