2011-04-05 35 views
2

比方說,我有以下代碼:Can/do C編譯器能夠優化內聯函數的地址嗎?

int f() { 
    int foo = 0; 
    int bar = 0; 

    foo++; 
    bar++; 

    // many more repeated operations in actual code 
    foo++; 
    bar++; 

    return foo+bar; 
} 

抽象重複的代碼到一個單獨的功能,我們得到

static void change_locals(int *foo_p, int *bar_p) { 
    *foo_p++; 
    *bar_p++; 
} 

int f() { 
    int foo = 0; 
    int bar = 0; 

    change_locals(&foo, &bar); 
    change_locals(&foo, &bar); 

    return foo+bar; 
} 

我期望編譯器內聯的change_locals功能,並優化之類的東西*(&foo)++在產生的代碼foo++

如果我沒有記錯,取一個局部變量的地址通常會阻止某些優化(例如它不能存儲在寄存器中),但是當這個地址沒有指針算術完成並且它不能逃脫從功能?對於較大的change_locals,如果它被宣佈爲inline(MSVC中爲__inline),它會有所作爲嗎?

我對GCC和MSVC編譯器的行爲特別感興趣。

+3

最好的辦法是嘗試檢查發射的組件。 – sharptooth 2011-04-05 05:48:51

回答

3

inline(及其所有表兄弟_inline__inline ...)是由GCC忽略。除了較低的優化級別之外,它可能內聯任何它認爲是優點的內容。

由86 GCC -O3代碼程序是:

 .text 
     .p2align 4,,15 
.globl f 
     .type f, @function 
f: 
     pushl %ebp 
     xorl %eax, %eax 
     movl %esp, %ebp 
     popl %ebp 
     ret 
     .ident "GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)" 

它返回零,因爲* PTR ++不做你的想法。更正增量:

(*foo_p)++; 
    (*bar_p)++; 

結果

 .text 
     .p2align 4,,15 
.globl f 
     .type f, @function 
f: 
     pushl %ebp 
     movl $4, %eax 
     movl %esp, %ebp 
     popl %ebp 
     ret 

所以直接返回4.它不僅內嵌他們,但它優化的計算了。

來自vs 2005的VC++提供了類似的代碼,但它也爲change_locals()創建了無法訪問的代碼。我使用的命令行

/O2 /FD /EHsc /MD /FA /c /TP 
1

我測試過的gcc 4.5,MSC和IntelC使用此:

#include <stdio.h> 

void change_locals(int *foo_p, int *bar_p) { 
    (*foo_p)++; 
    (*bar_p)++; 
} 

int main() { 
    int foo = printf(""); 
    int bar = printf(""); 

    change_locals(&foo, &bar); 
    change_locals(&foo, &bar); 

    printf("%i\n", foo+bar); 
} 

,他們都沒有內嵌/優化FOO +酒吧的價值,但也沒 產生change_locals代碼()(但沒有使用它)。

不幸的是,他們仍然不能保證他們會爲 做任何類似的「本地功能」。

GCC:

__Z13change_localsPiS_: 
    pushl %ebp 
    movl %esp, %ebp 
    movl 8(%ebp), %edx 
    movl 12(%ebp), %eax 
    incl (%edx) 
    incl (%eax) 
    leave 
    ret 

_main: 
    pushl %ebp 
    movl %esp, %ebp 
    andl $-16, %esp 
    pushl %ebx 
    subl $28, %esp 
    call ___main 
    movl $LC0, (%esp) 
    call _printf 
    movl %eax, %ebx 
    movl $LC0, (%esp) 
    call _printf 
    leal 4(%ebx,%eax), %eax 
    movl %eax, 4(%esp) 
    movl $LC1, (%esp) 
    call _printf 
    xorl %eax, %eax 
    addl $28, %esp 
    popl %ebx 
    leave 
    ret 
2

如果我沒有記錯,採取了局部變量的 地址通常 防止一些優化(例如,它 不能存儲在寄存器),但 這是否適用時無指針 算術完成地址和 它不會逃離函數?

一般的答案是,如果編譯器可以確保沒有其他人在背後改變一個值,它可以安全地放在寄存器中。

將這看作雖然編譯器首先執行內聯,然後變換所有那些*&foo(從內聯導致)簡單地foo確定是否應將在存儲器被放置在寄存器堆棧上之前。

有了較大change_locals,將它 有所作爲,如果它被宣佈 在線(MSVC中__inline)?

此外,一般來說,編譯器是否決定使用啓發式方法來內聯某些內容。如果你明確指定你想要某些內聯,編譯器可能會將其加入到決策過程中。