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