[英]When will the trivial (code that has no effect) code gets removed in compilation process?
volatile int num = 0;
num = num + 10;
上面的C ++代碼似乎在intel匯編中產生以下代碼:
mov DWORD PTR [rbp-4], 0
mov eax, DWORD PTR [rbp-4]
add eax, 10
mov DWORD PTR [rbp-4], eax
如果我將C ++代碼更改為
volatile int num = 0;
num = num + 0;
為什么編譯器不生成匯編代碼,如下所示:
mov DWORD PTR [rbp-4], 0
mov eax, DWORD PTR [rbp-4]
add eax, 0
mov DWORD PTR [rbp-4], eax
gcc7.2 -O0
省略了add eax, 0
,但其他所有指令都相同(Godbolt) 。
這類瑣碎的代碼在編譯過程的哪一部分被刪除。 是否有任何編譯器標志會使GCC編譯器不進行此類優化。
clang將在-O0
處發出add eax, 0
,但gcc,ICC或MSVC都不發出。 見下文。
gcc -O0
並不意味着“沒有優化”。 gcc沒有嘗試將每個C表達式的每個組件直接音譯為asm指令的“ braindead文字翻譯”模式。
GCC的-O0
並非要完全未優化。 它旨在“快速編譯”,並使調試產生預期的結果(即使您使用調試器修改C變量,或跳轉到函數中的另一行)。 因此,它會溢出/重新加載每個C語句周圍的所有內容,假設可以通過在此類塊之前停止的調試器來異步修改內存。 (結果的有趣示例,以及更詳細的解釋: 為什么將-1除以整數(負數)會導致FPE? )
對於gcc -O0
編寫更慢的代碼沒有太多需求(例如,忘記0
是加法標識),因此沒有人為此實現選項。 如果該行為是可選的,甚至可能會使gcc變慢。 (或者也許有這樣一個選項,但是即使在-O0
,它也是默認情況下處於啟用狀態,因為它速度快,不會損害調試且非常有用。通常人們喜歡它的調試版本運行得足夠快時才可以使用,特別是對於大型或大型實時項目。)
正如@Basile Starynkevitch在“ 禁用GCC中的所有優化選項”中解釋的那樣,gcc始終在生成可執行文件的過程中通過其內部表示進行轉換。 完全執行此操作會導致某些優化。
例如, 即使在-O0
,gcc的“除以常數”算法也使用定點乘法逆或移位(對於2的冪),而不是idiv
指令。 但是clang -O0
將對x /= 2
使用idiv
。
在這種情況下,Clang的-O0
也會比gcc的優化少:
void foo(void) {
volatile int num = 0;
num = num + 0;
}
push rbp
mov rbp, rsp
# your asm block from the question, but with 0 instead of 10
mov dword ptr [rbp - 4], 0
mov eax, dword ptr [rbp - 4]
add eax, 0
mov dword ptr [rbp - 4], eax
pop rbp
ret
如您所說,gcc忽略了無用的add eax,0
。 ICC17多次存儲/重新加載。 MSVC在調試模式下通常是極其文字化的,但是即使這樣,它也避免發出add eax,0
。
Clang也是Godbolt上唯一使用idiv
return x/2;
的4個x86編譯器之一return x/2;
。 其他的全部是SAR + CMOV或任何實現C的帶符號分隔語義的東西。
按照C ++中的“好像”規則,只要可觀察到的行為與標准相匹配,就可以自由地允許實現執行其想要的任何事情。 具體來說,在C++17, 4.6/1
(作為示例):
……需要遵循的實現來(僅)模擬抽象機的可觀察行為,如下所述。
該規定有時稱為“按原樣”規則,因為只要可以根據可觀察到的行為確定結果,就可以無視本國際標准的任何要求,而該實現可以自由地執行。該程序。
例如,如果實際實現可以推斷出未使用其值並且不會產生影響程序可觀察行為的副作用,則無需評估表達式的一部分。
關於如何控制gcc
,我的第一個建議是使用-O0
標志關閉所有優化。 您可以使用各種-f<blah>
選項來獲得更好的控制效果,但是-O0
應該是一個好的開始。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.