編譯器本身是如何進行測試的?

編譯器面對的輸入非常情況非常多,再比如C/C++有複雜的編譯優化,編譯器是如何確保經過優化後的代碼沒有問題,編譯器本身是如何進行測試的?


這段一直在CLR搞新的jit的研發,感覺可以小答一下。當編譯器新的feature完成後,首選會進行跑專用編譯器測試,這種我們組大概有8000多個,似於1+1是不是等於2呀,或者循環是不是跑了那麼多次,感覺很多都是驗證性測試,簡單的說不管你優化做的怎麼樣,至少別讓生成出來的代碼是得到錯誤的結果。之後進行asm diff測試,主要是用編譯器對各種庫生成新的機器碼,與之前版本的版本進行比對,這個時候就可以驗證你的實現的優化演算法是不是有效,這個目前感覺沒啥特別有效的辦法大部分時候就是人工進行比對,這個也非常噁心的,看很多機器碼對比,有的change只優化幾處還要,要是改變寄存器分配這種,那工作量可是相當。。。之後還會看看代碼生成出來的大小,看看throughput之類的。再之後就是各種壓力測試,雖然會用同樣的test case, 但會故意限制一些資源,比如原本可以用20個寄存器,現在只讓用5個,在生成flowgraph的時候故意產生很奇怪的權重等等,看看編譯器能不能承受,這個涉及到很多實現細節,就不多說了。之後還會有GC測試,但這塊不是非常熟也不細說了。然後,這些都完了之後還會跑一些隨機程序,比如我們組內部有一個工具會隨機產生IL,然後把這些隨機的IL丟給jit看看會不會產生assert錯誤。最後會跑整個CLR全部的測試文件,比如profiler的,type system的,等等總共加起來有2萬多個吧,有時候也會把先開發的編譯器發給bing呀,Roslyn呀,讓他們用一下跑一下他們的測試或者就地把玩一下。順便吐槽一下,這個時候出個bug可真是噁心呀,比如某個文件平時編譯沒事,一ngen就出錯,想想看幾個G的文件要找出錯那一行機器碼,那感覺可是相當刺激,再比如有時候給debugger的輸出的代碼有bug,經常是開著windbg,debug 我們內部的debugger mdbg,然後mdbg還在debug新Jit出來的代碼,說實話真的能把人噁心死。先說這麼多,如果有什麼不準確的地方歡迎大家友好討論。


Csmith

自動生成隨機的test case來做fuzzing,如果多個編譯器給出的目標程序運行結果不同,那就說明編譯器有bug。詳情見PLDI11的Finding and Understanding Bugs in C Compilers一文。


還是主要是跑Test Buckets,如C++的話,就會有編譯類,運行類,性能類等。編譯類的話,就會有是否可以編譯通過,是否會報指定的信息等,這一步驟主要是用於語法層面的測試,尤其是C++11出來後。運行類主要指跑出來的結果是否符合預期,如最後的結果,返回值等。性能類的話,主要是跑這個程序所消耗的時間,乃至內存等,一般是跑SPEC(比如SPEC2006),這個一般會跑很久,我跑SPEC都是開個screen,然後讓它自動跑。正是如此,Test Buckets需要足夠的大,足夠的豐富,乃至千奇百怪,而正是如此,甚至有專門賣這樣的Test Buckets的,比如Glen McCluskey C++ Test Buckets,看了這個Test Buckets,我都在佩服寫這個的人,是怎麼想出來這些奇葩Case的。


除了人工測試,我接觸到的很多公司也在開展自動測試。自動測試的基本原理就是隨機生成程序,然後用不同的方式編譯出運行等價的程序,然後再對比。具體做法包括:

* 用兩個不同的編譯器編譯,對比運行結果

* 用一個編譯器的不同優化選項編譯,對比運行結果

* (由知名華人科學家蘇振東教授設計)對原來程序中不被運行的語句隨便亂改,然後和原來的程序對比執行結果。

我們15年有一篇論文對以上三種方法的效果做對比(http://sei.pku.edu.cn/~xiongyf04/papers/ICSE16.pdf)。

當然,高票回復裡面提到的檢查assertion在這個過程中也順便做了。

隨機程序的自動生成有很多不同的工具,最著名的開源工具是華為的楊學軍開發C自動生成工具CSmith,而一般的大公司往往會有自己語言的專用生成工具。

因為編譯器通常都比較成熟,自動測試的周期基本很長。有實力的公司會撥一個集群專門負責測試,這個集群24小時不間斷生成程序並測試最新的版本,測試的結果另有一個團隊分析並提交給開發人員。目前這個集群的維護成本也是測試很高的一塊成本,一些中小公司曾向我們表示希望找到辦法能降下來。我們17年有一篇論文想辦法加速測試過程,降低公司在集群上的成本(http://sei.pku.edu.cn/~xiongyf04/papers/ICSE17b.pdf)。


有一些測試套件用於測試編譯器是否符合語言標準。比如Plum Hall, Inc. 這家公司提供一些工具用來測試C/C++,Java和C#編譯器是否滿足語言標準,能處理標準中描述的所有特性等等。

通常就是很多的代碼段。你可以自己下了GCC看看,它中自帶的GNU Torture就是一堆的測試用代碼。C Tests - GNU Compiler Collection (GCC) Internals這裡描述了各個目錄內的代碼都是用來測試哪些特性的。

另外,也可以參考ARM編譯器通過的測試。編譯器驗證 - ARM這裡描述了ARM的編譯器是如何測試發布的。其中所描述的各個工具也都可以自己搜來看看。

編譯器也是軟體,測試和別的軟體差不了多少。都是給個輸入,看看能不能正確輸出。給個錯誤輸入看看能不能給出提示或者處理等等。只不過測試的輸入是一些代碼而已。


曾經聽說codeforces過去發現了一些gcc新版本的bug,後來會在gcc發布新版本時,嘗試加優化編譯運行一些代碼,看程序運行結果與之前版本的是否相同……


https://arxiv.org/pdf/1610.03148.pdf

這篇文章的technique發現了兩百多個gcc+clang的bug.


謝邀.

不知道.

不過......如何測試一個編譯器?


用它去編譯一個程序。

這個程序由各種各樣的語法寫成。

看其運行結果是否等於程序的理論值。


@藍色大大講到的test buckets,以及@熊英飛老師講到的學術界的一些新方法,他們說的都比較詳細了。

我還想補充一點,就是編譯gcc時用到的bootstrap,詳細見gcc官方wiki.


如果懂go,可以參考go源代碼編譯器部分自帶的測試代碼


編譯器測試是很難的,首先不同於一般軟體,錯誤有兩種,編譯時錯和運行時錯,而且還很容易死鎖。不光要測正確性,還要測性能。性能測試的標準有時候真的很多,而正確性測試要包含各種極端和複雜的情況。一個編譯器的要求高,所以自身複雜性就高,於是就需要更多測試。

具體的測試內容有自帶的單元測試,語言特性測試,隨機測試,各種測試集的性能,等等。

由於以上種種,編譯器的內部實現最好敏感於各種異常情況,有不對馬上崩潰,因為編譯時錯比運行時錯實在是好太多。


分正確性測試和性能測試

正確性可以用各種隨機生成的code,看最後結果是不是正確,有一定生成規則。

性能測試就是各種跑分軟體,具體每個優化有沒有生效,還可以加focus case。


如果能成功編譯一兩個大項目的話基本上99.9999%的bug都沒有了,現在主要問題不是編譯器,而是framework中的某些細小bug,這些反而更致命


推薦閱讀:

如何學習 clang和LLVM(有關於源代碼閱讀),需要哪些知識?
有什麼好用的C/C++源代碼混淆工具?
生成編譯器的代碼又由誰負責編譯?
如何看待Twitter將重新實現Scala編譯器?
計算機編程語言必須能夠自舉嗎?

TAG:軟體測試 | 計算機科學 | 編譯器 |