【转】C 编译器优化过程中的 Bug
C 編譯器優化過程中的 Bug
一個朋友向我指出一個最近他們發現的 GCC 編譯器優化過程(加上 -O3 選項)里的 bug,導致他們的產品出現非常詭異的行為。這使我想起以前見過的一個 GCC bug。當時很多人死活認為那種做法是正確的,跟他們說不清楚。簡言之,這種有問題的優化,喜歡利用 C 語言的“未定義行為”(undefined behavior)進行推斷,最后得到奇怪的結果。
這類優化過程的推理方式都很類似,他們使用一種看似嚴密而巧妙的推理,例如:“現在有一個整數 x,我們不知道它是多少。但 x 出現在一個條件語句里面,如果 x > 1,那么程序會進入未定義行為,所以我們可以斷定 x 的值必然小于或者等于 1,所以現在我們利用 x ≤ 1 這個事實來對相關代碼進行優化……”
看似合理,然而它卻是不正確的,你能看出來這樣的推理錯在何處嗎?我一時想不起來之前具體的例子了(如果你知道的話告訴我)。上網搜了一下相關話題,發現這篇 Chris Lattner (LLVM 和 Swift 語言 的設計者)?寫于 2011 年的文章。文中指出,編譯器利用 C 語言的“未定義行為”進行優化,是合理的,對于性能是很重要的,并且舉出這樣一個例子:
void contains_null_check(int *P) { int dead = *P; if (P == 0) return; *P = 4; }這例子跟我之前看到的 GCC bug 不大一樣,但大致是類似的推理方式:這個函數依次經過這樣兩個優化步驟(RNCE 和 DCE),之后得出“等價”的代碼:
void contains_null_check_after_RNCE(int *P) { int dead = *P; if (false) // P 在上一行被訪問,所以這里 P 不可能是 null return; *P = 4; } void contains_null_check_after_RNCE_and_DCE(int *P) { //int dead = *P; // 死代碼消除 //if (false) // 死代碼 // return; // 死代碼 *P = 4; }他的推理方式是這樣:
最后函數就只剩下一行代碼 *P = 4。然而經我分析,發現這個優化轉換是根本錯誤的做法(unsound 的變換),而不只是像他說的“存在安全隱患”。現在我來考考你,你知道這為什么是錯的嗎?值得慶幸的是,現在如果你把這代碼輸入到 Clang,就算加上 -O3 選項,它也不會給你進行這個優化。這也許說明 Lattner 的這個想法后來已經被 LLVM 團隊拋棄。
我寫這篇文章的目的其實是想告訴你,不要盲目的相信編譯器的作者們做出的變換都是正確的,無論它看起來多么的合理,只要打開優化之后你的程序出現奇葩的行為,你就不能排除編譯器進行了錯誤優化的可能性。Lattner 指出這樣的優化完全符合 C 語言的標準,這說明就算你符合國際標準,也有可能其實是錯的。有時候,你是得相信自己的直覺……
轉載于:https://www.cnblogs.com/alantu2018/p/8547506.html
總結
以上是生活随笔為你收集整理的【转】C 编译器优化过程中的 Bug的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python3网络爬虫开发实战】3-基
- 下一篇: 前端之javaScript