0

考虑以下 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,使用 中的寄存器访问foo,并在 中完全忽略bar。GCC 生成的代码用于foo. 但是, in barc从寄存器复制到堆栈,然后立即被忽略:

    .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

(注意aandb被传入%rsiand %rdi, andc被传入%rcxand %rdx。)

我可以推测的唯一原因是某种 ABI 要求(例如与 longjmp 交互)。我找不到 GCC 的任何优化 ( -f) 选项,也找不到抑制这种行为的 GCC 特定注释。c用注释register没有帮助。

这也发生在不同的目标上。(值得注意的是,在 TileGXfoo上,堆栈上已分配和释放空间,但没有存储任何内容。)我测试了 GCC 4.4.6 和 4.6.1。

这是预期的行为还是 GCC 中的错误?无论哪种方式,有没有办法解决它(除了使用引用调用或确保bar可以是一片叶子)?

4

1 回答 1

0

这个缺点与错误44194中提到的相同,该补丁存在于最新版本的 GCC (4.7.2) 中。

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

于 2012-11-10T03:14:53.720 回答