2012-11-09 69 views
0

考慮以下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在寄存器中傳遞到兩個foobar,在foo使用寄存器的訪問,並在被完全忽略bar。 GCC產生的代碼是foo。然而,在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 

(注意:ab%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可以是葉)?

回答

0

這個缺點與錯誤44194中提到的相同,其中的補丁出現在最新版本的GCC(4.7.2)中。

原因大致在於調用printf(或任何函數)被認爲能夠訪問內存中的任何內容,包括基於堆棧的本地語言。該修補程序會導致基於堆棧的本地不被調用者視爲可訪問。