<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 探索 GCC 前端的內部結構(三)

          探索 GCC 前端的內部結構(三)

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

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

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

          注釋開始::::: 

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

          :::::注釋結束 

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

          注釋開始::::: 

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

          :::::注釋結束 

          在 parse.y 中的一些主要的產生式上所匹配的 C 函數,它們所做的工作大體上都是首先根據語法分析的結果,把自己定義的結構 struct prod_token_parm_item 里面的數據先給設置好;然后根據情況調用在 treetree.c 中定義的相關函數,生成 tree 結構的數據;這之后再把返回來的 tree 結構數據記錄在 struct prod_token_parm_item 里面,并把整個結構的數據放到 symbol_table 這個單向鏈表上。這樣看來,似乎這個 symbol_table 就是我們前面所要尋找的全局變量了。是不是在語法分析任務完成以后,就獲得了這個全局變量;然后依賴于這個全局變量,后續(xù)任務才得以獲得輸入數據,繼續(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 的英文版的第五版第八十三頁上,關于 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 的主體結構上,經由 lang_hooks 進入 treelang 部分的 yyparse 函數,這個函數按照語法定義,把編譯器用戶輸入的 Treelang 語言的源程序分解成若干類型的小塊,加以分析,生成自己定義的 struct prod_token_parm_item 結構的數據,再把這些數據一個一個串到 symbol_table 這個鏈表上面;這樣就算完成任務了。線索從 lang_hooks 中定義的這個回調函數撤出,再度回到 GCC 的主體框架。 

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

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

          語法分析的細節(jié)

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

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

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

          GCC 前端的全景圖

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

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

          小結

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

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

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


          評論


          相關推薦

          技術專區(qū)

          關閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();