簡體   English   中英

為什么編譯器不能優化這些無法訪問的指令?

[英]Why doesn't the compiler optimize out these unreachable instructions?

鑒於此代碼:

int x;

int a (int b) {
    b = a (b);
    b += x;
    return b;
}

為什么GCC會返回此輸出(英特爾語法): http//goo.gl/8D32F1 - Godbolt的GCC Explorer

a:
    sub rsp, 8
    call    a
    mov edx, DWORD PTR x[rip]
    add rsp, 8
    lea eax, [rax+rdx*8]
    add eax, edx
    ret

和Clang返回此輸出(AT&T語法): http//goo.gl/Zz2rKA - Godbolt的Clang Explorer

a:                                      # @a
    pushq   %rax
    callq   a
    addl    x(%rip), %eax
    popq    %rdx
    ret

當部分代碼明顯無法訪問時? 因為函數的第一個陳述是

b = a (b);

該函數將永遠保持遞歸調用自身(直到堆棧溢出,你得到一個段錯誤)。 這意味着您永遠不會超越該行,因此,其余代碼無法訪問。 可達性優化理論上應該刪除代碼,對嗎?


兩個編譯器都在x64上運行,並帶有以下標志

  • -O3 - 最大化優化
  • -march=native - [不必要]盡可能使用機器特定的優化
  • -xc - 假設輸入語言為C.

我當時認為他們應該返回更多內容(雙關語):

GCC(英特爾語法):

a:
.L1:
    jmp .L1

Clang(AT&T語法):

a:
.LBB0_1:
    jmp .LBB0_1

注意:這些樣本是從以前觀察的記憶中手工編寫的,可能不正確。


總的來說,為什么沒有任何一個編譯器將函數折疊成單個遞歸跳轉,因為其余代碼無法訪問?


編輯:

回應傑克關於語義等價的評論:

對於以下代碼:

int j (int x) {
    while (1) {};
    x++;
    return x;
}

GCC返回: http//goo.gl/CYSUW2

j:
.L2:
    jmp .L2

Clang回歸:

j:                                      # @j
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
    jmp .LBB0_1

回應亞當關於吹掉堆棧的評論:

對於此代碼:

int r (int x) {
    return r (x);
}

GCC生成遞歸跳轉: http//goo.gl/eWo2Nb

r:
.L2:
    jmp .L2

Clang很早就回來了: http//goo.gl/CVJKiZ

r:                                      # @r
    ret

您正在使用的編譯器可能僅在單個函數框架內的塊級別實現數據流分析,而不考慮遞歸。 (或許,只有有趣的遞歸,即尾遞歸。)由於遞歸調用不是尾調用,從優化的角度來看它並不有意思。

你的函數有一個問題:它的編譯方式,它會炸毀堆棧。 它是這樣編譯的,因為調用不是尾調用; 將它視為一個合法的優化並不合理。

該調用可以被認為是“偽尾調用”,理由是調用后的代碼永遠不會被調用,因此如果我們刪除該代碼,那么遞歸調用是函數執行的最后一件事。 然后我們可以將堆棧代碼減少到僅僅是無限循環。 但是,這實際上不能稱為優化; 它是由不同的bug表現形式替換一個bug表現形式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM