为什么GCC坚持非叶子callees将CBV结构复制到堆栈?

时间:2012-11-09 19:11:57

标签: optimization gcc call cpu-registers

考虑以下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在寄存器中传递给foobar,使用{{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可以是叶子)?

1 个答案:

答案 0 :(得分:0)

这个缺点与bug 44194中提到的相同,其补丁出现在最新版本的GCC(4.7.2)中。

原因大致是对printf(或任何函数)的调用被认为能够访问内存中的任何内容,包括基于堆栈的本地。该补丁会导致被调用者无法认为基于堆栈的本地人可以访问。