新聞中心

EEPW首頁 > 探索 GCC 前端的內(nèi)部結(jié)構(gòu)(三)

探索 GCC 前端的內(nèi)部結(jié)構(gòu)(三)

——
作者: 時間:2007-04-17 來源:嵌入開發(fā)網(wǎng) 收藏
對用戶源文件進行語法分析

這個 treelang 注冊的這些回調(diào)函數(shù)在 GCC 主框架那里被調(diào)用的順序,我們暫時還不想深入。揀有意思的先看看吧。首先關(guān)注的是 treelang_parse_file 這個函數(shù)。在 langhooks.h 里面關(guān)于這個回調(diào)函數(shù)所作的注釋說明,是要它對用戶的整個源文件進行語法分析。因為這個函數(shù)的返回值是 void 所以我們預(yù)期它是通過設(shè)置某一個全局變量來完成任務(wù)的;但是也有另外一種可能,就是它會把所有要做的事情都給做完,這樣它也就自然不需要返回值了。這兩種可能我們現(xiàn)在還不能確定。讓我們往下看吧。 

這個 treelang_parse_file 函數(shù)在 tree1.c 中定義,這是屬于到 GCC 前端的接口。它直接就跑去調(diào)用 yyparse 這個 YACC 主函數(shù)了。這倒是簡單,呵呵。可是要我們從 parse.y 文件中理出個頭緒來,這個文件有超過 900 行的 YACC 代碼,未免有點麻煩。最關(guān)鍵的是這中間數(shù)據(jù)的交流不大容易看清楚,不像回調(diào)函數(shù)指針這樣顯而易見。如果程序果真是通過設(shè)置一些全局變量來完成任務(wù)的話,我們的分析任務(wù)就有點棘手了。 

注釋開始::::: 

在這里先說一下 tree 這個數(shù)據(jù)結(jié)構(gòu)。這是 GCC 圍繞著 C 和 C++ 語言的語法分析,用到的主要數(shù)據(jù)結(jié)構(gòu)。所有其它語言的前端,也都需要在語法分析階段結(jié)束以后,為 GCC 生成相應(yīng)的 tree 結(jié)構(gòu)的數(shù)據(jù)。然后 GCC 的后端就可以從 tree 生成獨立于平臺的 RTL 數(shù)據(jù)結(jié)構(gòu),并隨后生成相應(yīng)平臺上的機器語言代碼。所以作為 GCC 的前端,這里的主要工作就是從一個文本文件,也就是源代碼,生成這個 tree 結(jié)構(gòu)的數(shù)據(jù),喂給的后端。我們看到,前端是依賴于編程語言的;后端是依賴于機器平臺的;中間的 tree 和 RTL 則獨立于編程語言和機器平臺。但是話雖如此說,這個 tree 和 RTL 數(shù)據(jù)結(jié)構(gòu)也還是主要以 C 和 C++ 語言為考慮問題的中心。這是不可避免的事情。 

:::::注釋結(jié)束 

好啦,沒辦法啦。我們這就開始從 treelang 目錄下的 parse.y 一行一行的往下瞅吧。這個 Treelang 程序語言的語法很簡單,我們看到哪兒,說到哪兒。 

注釋開始::::: 

在看 GCC 的源代碼的時候經(jīng)常會遇到 GTY 這個東西。這是 GCC 內(nèi)部的內(nèi)存管理機制所需要的,在 C 語言代碼上添加的一些類型信息,這些類型信息在 GCC 內(nèi)部做垃圾收集的時候會用到。這個細節(jié)我們這里先忽略過去,以后講到相關(guān)內(nèi)容的時候再做說明。 

:::::注釋結(jié)束 

在 parse.y 中的一些主要的產(chǎn)生式上所匹配的 C 函數(shù),它們所做的工作大體上都是首先根據(jù)語法分析的結(jié)果,把自己定義的結(jié)構(gòu) struct prod_token_parm_item 里面的數(shù)據(jù)先給設(shè)置好;然后根據(jù)情況調(diào)用在 treetree.c 中定義的相關(guān)函數(shù),生成 tree 結(jié)構(gòu)的數(shù)據(jù);這之后再把返回來的 tree 結(jié)構(gòu)數(shù)據(jù)記錄在 struct prod_token_parm_item 里面,并把整個結(jié)構(gòu)的數(shù)據(jù)放到 symbol_table 這個單向鏈表上。這樣看來,似乎這個 symbol_table 就是我們前面所要尋找的全局變量了。是不是在語法分析任務(wù)完成以后,就獲得了這個全局變量;然后依賴于這個全局變量,后續(xù)任務(wù)才得以獲得輸入數(shù)據(jù),繼續(xù)往下執(zhí)行呢? 

我們來仔細看一看 tree1.c 中這個 symbol_table 變量的定義如下。 



static GTY(()) struct prod_token_parm_item *symbol_table = NULL;

 



注意到這是被申明為 static 的變量。在 Samuel P. Harbison III 和 Guy L. Steele, Jr 所合著的 C: A Reference Manual 的英文版的第五版第八十三頁上,關(guān)于 static 變量有如下說明:"On data declarations, it always signifies a defining declaration that is not exported to the linker."換句話說,這個 static 的 symbol_table 變量,在 tree1.o 之外是看不見的。這不可能是我們所要尋找的全局變量。 

可是,另一方面,除了這個變量有點像是那么一回事之外,其它的就再也沒有什么有趣的變量了。這是怎么一回事呢?我們先不管它,往下看了再說吧。 

那么這個 parse.y 文件大體如是啦。其它的一些具體的細節(jié)問題,牽涉到 Treelang 程序語言的具體定義,暫且不是我們的興趣所在。粗粗的看一遍下來,這個語法分析的過程,從 GCC 的主體結(jié)構(gòu)上,經(jīng)由 lang_hooks 進入 treelang 部分的 yyparse 函數(shù),這個函數(shù)按照語法定義,把編譯器用戶輸入的 Treelang 語言的源程序分解成若干類型的小塊,加以分析,生成自己定義的 struct prod_token_parm_item 結(jié)構(gòu)的數(shù)據(jù),再把這些數(shù)據(jù)一個一個串到 symbol_table 這個鏈表上面;這樣就算完成任務(wù)了。線索從 lang_hooks 中定義的這個回調(diào)函數(shù)撤出,再度回到 GCC 的主體框架。 

對了,上面還忘了說,在把用戶輸入的 Treelang 語言的源程序進行分解以后,在分析的過程中,按照各種類型的小塊,還生成了相應(yīng)的 tree 結(jié)構(gòu)的數(shù)據(jù),一起記錄在各自的 struct prod_token_parm_item 結(jié)構(gòu)里面,這樣就一并把這個 tree 結(jié)構(gòu)的數(shù)據(jù)也都放在了 symbol_table 這個鏈表里了。 

接下來回到 GCC 的主體框架上的 toplev.c 文件??墒敲曰笕说氖虑槌霈F(xiàn)了,在函數(shù) compile_file 對回調(diào)函數(shù) treelang_parse_file 進行調(diào)用之后,無論是在 toplev.c 文件中,還是說在哪一個其它的回調(diào)函數(shù)里也好,似乎都并沒有什么有趣的事情發(fā)生了。這讓我們?nèi)绾问呛??看來我們只有回過頭去仔細跟蹤 treelang 目錄下的 treetree.c 文件中的那些函數(shù),看看它們在被 parse.y 中的產(chǎn)生式調(diào)用執(zhí)行的時候,到底干了些什么。

語法分析的細節(jié)

根據(jù)從 parse.y 這個 YACC 文件中的產(chǎn)生式得來的線索,我們首先關(guān)注 treetree.c 文件中的 tree_code_create_variable 這個函數(shù)。從那個 YACC 產(chǎn)生式,我們估計這個函數(shù)是為一個變量申明而構(gòu)造必要的 tree 數(shù)據(jù)結(jié)構(gòu)。這個函數(shù)有 100 行不到的源代碼。我們來仔細的看一看。這個函數(shù)使用了從 GCC 的框架結(jié)構(gòu)里面來的關(guān)于 tree 數(shù)據(jù)結(jié)構(gòu)的一些 API 接口。我們目前所最感興趣的,就是這個函數(shù)在利用這些接口函數(shù)構(gòu)造一個和所對應(yīng)的 YACC 產(chǎn)生式相當?shù)?nbsp;tree 結(jié)構(gòu)數(shù)據(jù)以外,還干了些什么。我們之所以關(guān)心這個"以外",是因為目前我們最想了解的,是這個從 Treelang 語言的源程序開始,到一連串的 tree 結(jié)構(gòu)數(shù)據(jù),然后是怎么變成 RTL 結(jié)構(gòu)的數(shù)據(jù)的。只有在有了這樣一個概觀以后,我們對 GCC 前端的編寫方法才能算有了一個初步的大概的了解。 

根據(jù)這樣的思路,我們很快就看清楚,在這個 tree_code_create_variable 函數(shù)中,在設(shè)置好若干個局部的 tree 結(jié)構(gòu)的數(shù)據(jù)以后,引人注目的在一個 if 語句的分支中調(diào)用了 rest_of_decl_compilation 這個函數(shù)。而且在這個函數(shù)被調(diào)用返回以后,似乎不再有重要的事情發(fā)生了。這個函數(shù)來自于 GCC 框架結(jié)構(gòu)上的 toplev.c 文件。這樣的話,根據(jù)我們前面的分析,這個函數(shù)里面應(yīng)該會隱藏有我們的主要問題的答案。也就是說,在 YACC 文件 parse.y 把用戶提供的 Treelang 語言的源文件肢解以后,在 treetree.c 中的相應(yīng)的函數(shù),為之生成了相應(yīng)的 tree 結(jié)構(gòu)數(shù)據(jù),而在現(xiàn)在我們所關(guān)注的這個 rest_of_decl_compilation 函數(shù)(以及在這個 if 語句的另一個分支中出現(xiàn)的一系列相應(yīng)的函數(shù))中,應(yīng)該會完成從 tree 結(jié)構(gòu)的數(shù)據(jù)到 RTL 數(shù)據(jù)的翻譯。 

從另一個角度補充一點,程序的執(zhí)行線索是如何從 GCC 主框架進入 parse.y 中的呢?這一段我們前面分析過了,現(xiàn)在再來提醒一下。這是從 GCC 的框架結(jié)構(gòu),進入到 treelang 這個 GCC 的語言前端模塊注冊的 lang_hooks 結(jié)構(gòu)的數(shù)據(jù),找到相應(yīng)的回調(diào)函數(shù),最終找到 parse.y 這個 YACC 程序的入口 yyparse 函數(shù)的。在 yyparse 之后,我們看到程序的主線索進入了 treelang 目錄下的 treetree.c 文件中的函數(shù)。最后,我們重新又追蹤到 GCC 主體部分的 toplev.c 文件中的函數(shù)?,F(xiàn)在我們的整個圖景的大輪廓就快要完全弄清楚了。

GCC 前端的全景圖

終于,我們在 rest_of_decl_compilation 函數(shù)中,看到了一系列的和 RTL 相關(guān)的函數(shù)調(diào)用。稍微仔細的看了一遍之后,我們有把握得出這個結(jié)論了。我們在本文的開頭部分,曾經(jīng)猜想 GCC 的主體部分在要求 GCC 這個 Treelang 語言前端從用戶提供的 Treelang 語言的源程序文本,經(jīng)過語法分析,得出相應(yīng)的 tree 結(jié)構(gòu)數(shù)據(jù)以后,會把這個數(shù)據(jù)通過函數(shù)返回值傳回給 GCC 的主體程序,或者設(shè)置一個全局變量,這樣就算完成任務(wù)了。但是事實上,經(jīng)過我們上面的分析,發(fā)現(xiàn)不是這么一回事。 

相反的,在 Treelang 這個語言前端得到需要的 tree 結(jié)構(gòu)的數(shù)據(jù)以后,繼續(xù)往下的運行,這完全是 Treelang 前端必須自己負責(zé)的任務(wù)。這個 GCC 前端必須自己調(diào)用 GCC 主體部分提供的,用來從 tree 結(jié)構(gòu)數(shù)據(jù)生成 RTL 結(jié)構(gòu)數(shù)據(jù)的函數(shù)接口,以完成從 tree 結(jié)構(gòu)數(shù)據(jù)到 RTL 結(jié)構(gòu)數(shù)據(jù)的翻譯過程。這樣,這個 GCC 的語言前端的任務(wù)才算完成。換句話說,GCC 的這個語言前端承擔(dān)的角色是非常的主動的。很明顯,這樣的設(shè)計提供給我們極大的靈活性。關(guān)于這一點,我們以后會逐漸看到。 

小結(jié)

本文限于篇幅,只大略講述了 GCC 前端的框架結(jié)構(gòu),給出了一個粗略的全景圖。在以后的幾篇文章中,我們將進一步探索 GCC 的主體部分為 GCC 前端所提供的 API 函數(shù)和數(shù)據(jù)結(jié)構(gòu)。并利用這些知識,探索一下為 GCC 編寫一個 Scheme 語言前端的可能性。在這一系列文章結(jié)束的時候,希望能使得讀者朋友們對 GCC 以及程序語言的本質(zhì)有一個更加深刻的了解。也希望 GCC 的前端的作者人數(shù),就能和 Linux 內(nèi)核模塊的作者人數(shù)一樣多。我們的座右銘是:每一個人都是程序員;每一個人都能加載自己編寫的內(nèi)核模塊;每一個人都能使用自己實現(xiàn)的編程語言?。ú灰ε?,這只是一句玩笑話。呵呵。) 

在技術(shù)內(nèi)容以外,本文也探索了開放源碼運動所需要的技術(shù)文檔的一種寫作模式。開放源碼運動為我們帶來了大量的自由軟件的源程序。對于用戶來說,需要文檔講述如何使用這些自由軟件;對于程序員來說,則需要文檔講述如何才能理解并真正的掌握這些自由軟件的源程序。這第二種文檔的寫作,不是一件容易的事情。作者本人在經(jīng)常閱讀解釋自由軟件的源程序的內(nèi)部運作機理的文檔的過程中,總是覺得這件事情應(yīng)該可以有辦法做的更好。本文就是作者的一個嘗試。希望讀者朋友們給我來信,不僅僅討論 GCC 的技術(shù)問題,也歡迎對作者的寫作方式提出批評與指教!
 

-------------------------------------------------------------------------------- 
 


評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉