GCC原始碼分析(二)——前端
原文連結:http://blog.csdn.net/sonicling/article/details/6706152
從這一篇開始,我們將從原始碼的角度來分析GCC如何完成對C語言原始檔的處理。GCC的內部構架在GCC Internals(搜“gccint.pdf”,或者見[1])裡已經講述得很詳細了,但是如果你只看了gccint就來看程式碼,還是覺得一頭霧水,無法下手,因為你很難把gccint所講的概念同gcc程式碼裡真實的資料結構聯絡起來。那麼這也是我把我這半年的分析經理寫下來的原因,大家可以參照gccint來看。那麼gccint中已經詳細講過的概念,在這裡就一筆帶過,這裡只研究GCC的原始碼。
一、原始碼組織
GCC的原始碼檔案非常多,總數大約有好幾萬。但是很多都是testsuite和lib。首先我們除去所有的testsuite目錄,然後lib打頭的目錄也可以基本上不看,那是各程式語言的gcc版標準庫和專為某種語言的編譯而設計的庫。我們只分析C語言的話,只用看其中的libcpp,它包含了C/C++的詞法分析和預處理。剩下的GCC原始碼大多集中在config、gcc兩個目錄下。
config目錄是Makefile為各跨平臺編譯準備的配置目錄。
gcc目錄下除去gcc/config目錄外的其他檔案是各個語言的編譯器前端原始檔,一般放在各自語言命名的目錄下,例如cp(C++)、java、fortran等。唯一例外的是C語言,它的前端原始檔同GCC的通用檔案(包括中間表示、中間優化等)一起,散放在gcc目錄下。
gcc/config目錄是gcc在各種硬體或作業系統平臺下的後端原始檔,負責把GCC生成的中間表示轉換為各平臺相關的機器碼、位元組碼或其他目標語言。
那我們可以從gcc的原始碼組織上大致看出gcc之所以能支援眾多前端和後端的原因,它將各種語言的原始檔按照各自的方法分析完之後,表示為由GENERIC、GIMPLE、RTL組成的統一的中間結構,再由各種後端將統一的結構轉換為各自平臺對應的目標語言。
二、詞法分析
詞法分析,通俗講,就是給原始檔斷詞。我們將原始檔看作一個字元流,並交由詞法分析器進行斷詞,詞法分析器必須能夠輸出一個一個的詞,也叫做記號(token),每個記號至少有三個屬性:
1.值:即斷出的那一段字串
2.型別:關鍵字、識別符號、文字常量、符號等
3.位置:這個記號在當前檔案的第幾行,用於報錯。
在《編譯原理》裡面,詞法分析是和NFA、DFA、正則表示式聯絡起來的,他們屬於III型語言。根據詞法定義,我們手頭已經有很多工具可以實現詞法分析器的自動構造,這些自動構造的程式碼無一例外的使用了DFA的概念,即構造出來的詞法分析器一定是一個DFA,裡面包含了初始狀態、終結狀態和狀態的轉移,而這些狀態都是自動構造中抽象出來的符號或者數字,一般人很難看出這些狀態在詞法定義中的位置。所以這也是自動構造的缺點——貪圖構造的方便,一定帶來修改的成本。
而GCC的詞法分析是手工構造的,實現在libcpp/lex.c檔案中,其中最重要的那個函式是_cpp_lex_direct,他反應了GCC詞法分析器的核心結構。程式碼很長,我只貼一點片段。
[cpp] view plaincopy
- switch (c)
- {
- case ' ': case '\t': case '\f': case '\v': case '\0':
- result->flags |= PREV_WHITE;
- skip_whitespace (pfile, c);
- goto skipped_white;
- case '\n':
- if (buffer->cur < buffer->rlimit)
- CPP_INCREMENT_LINE (pfile, 0);
- buffer->need_line = true;
- goto fresh_line;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- {
- struct normalize_state nst = INITIAL_NORMALIZE_STATE;
- result->type = CPP_NUMBER;
- lex_number (pfile, &result->val.str, &nst);
- warn_about_normalization (pfile, result, &nst);
- break;
- }
- case 'L':
- case 'u':
- case 'U':
- case 'R':
- /* 'L', 'u', 'U', 'u8' or 'R' may introduce wide characters,
- wide strings or raw strings. */
- if (c == 'L' || CPP_OPTION (pfile, uliterals))
- {
- if ((*buffer->cur == '\'' && c != 'R')
- || *buffer->cur == '"'
- || (*buffer->cur == 'R'
- && c != 'R'
- && buffer->cur[1] == '"'
- && CPP_OPTION (pfile, uliterals))
- || (*buffer->cur == '8'
- && c == 'u'
- && (buffer->cur[1] == '"'
- || (buffer->cur[1] == 'R' && buffer->cur[2] == '"'))))
- {
- lex_string (pfile, result, buffer->cur - 1);
- break;
- }
- }
- /* Fall through. */
- case '_':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K':
- case 'M': case 'N': case 'O': case 'P': case 'Q':
- case 'S': case 'T': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- result->type = CPP_NAME;
- {
- struct normalize_state nst = INITIAL_NORMALIZE_STATE;
- result->val.node.node = lex_identifier (pfile, buffer->cur - 1, false,
- &nst);
- warn_about_normalization (pfile, result, &nst);
- }
- /* Convert named operators to their proper types. */
- if (result->val.node.node->flags & NODE_OPERATOR)
- {
- result->flags |= NAMED_OP;
- result->type = (enum cpp_ttype) result->val.node.node->directive_index;
- }
- break;
- case '\'':
- case '"':
- lex_string (pfile, result, buffer->cur - 1);
- break;
- case '/':
- /* A potential block or line comment. */
- comment_start = buffer->cur;
- c = *buffer->cur;
- if (c == '*')
- {
- if (_cpp_skip_block_comment (pfile))
- cpp_error (pfile, CPP_DL_ERROR, "unterminated comment");
- }
- else if (c == '/' && (CPP_OPTION (pfile, cplusplus_comments)
- || cpp_in_system_header (pfile)))
- {
- /* Warn about comments only if pedantically GNUC89, and not
- in system headers. */
- if (CPP_OPTION (pfile, lang) == CLK_GNUC89 && CPP_PEDANTIC (pfile)
- && ! buffer->warned_cplusplus_comments)
- {
- cpp_error (pfile, CPP_DL_PEDWARN,
- "C++ style comments are not allowed in ISO C90");
- cpp_error (pfile, CPP_DL_PEDWARN,
- "(this will be reported only once per input file)");
- buffer->warned_cplusplus_comments = 1;
- }
- if (skip_line_comment (pfile) && CPP_OPTION (pfile, warn_comments))
- cpp_error (pfile, CPP_DL_WARNING, "multi-line comment");
- }
- else if (c == '=')
- {
- buffer->cur++;
- result->type = CPP_DIV_EQ;
- break;
- }
- else
- {
- result->type = CPP_DIV;
- break;
- }
- if (!pfile->state.save_comments)
- {
- result->flags |= PREV_WHITE;
- goto update_tokens_line;
- }
-
相關推薦
GCC原始碼分析(二)——前端
原文連結:http://blog.csdn.net/sonicling/article/details/6706152 從這一篇開始,我們將從原始碼的角度來分析GCC如何完成對C語言原始檔的處理。GCC的內部構架在GCC Internals(搜“gccint.pdf”,或者見[
Flume NG原始碼分析(二)支援執行時動態修改配置的配置模組
在上一篇中講了Flume NG配置模組基本的介面的類,PropertiesConfigurationProvider提供了基於properties配置檔案的靜態配置的能力,這篇細說一下PollingPropertiesFileConfigurationProvider提供的執行時動態修改配置並生效的
GCC原始碼分析(五)——指令生成
原文連結:http://blog.csdn.net/sonicling/article/details/8246231 一、前言 又有好久沒寫了,的確很忙。前篇介紹了GCC的pass格局,它是GCC中間語言部分的核心架構,也是貫穿整個編譯流程的核心。在完成優化處理之
GCC原始碼分析(三)——中間語言
原文連結:http://blog.csdn.net/sonicling/article/details/7915301 一、前言 很忙,很久沒更新部落格了,繼續沒寫完的gcc分析,爭取在傳說將要用C++重寫的gcc 5出來之前初略分析完。 二、符號表(GENERI
GCC原始碼分析(四)——優化
原文連結:http://blog.csdn.net/sonicling/article/details/7916931 一、前言 本篇只介紹一下框架,就不具體介紹每個步驟了。 二、Pass框架 上一篇已經講了gcc的中間語言的表現形式。gcc 對中間語言
GCC原始碼分析(一)——介紹與安裝
原文連結:http://blog.csdn.net/sonicling/article/details/6702031 上半年一直在做有關GCC和LD的專案,到現在還沒做完。最近幾天程式設計的那臺電腦壞了,所以趁此間隙寫一點相關的分析和
Glide原始碼分析(二)——從用法來看之load&into方法
上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260 在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques
YOLOv2原始碼分析(二)
文章全部YOLOv2原始碼分析 接著上一講沒有講完的make_convolutional_layer函式 0x01 make_convolutional_layer //make_convolutional_laye
zigbee 之ZStack-2.5.1a原始碼分析(二) 無線接收控制LED
本文描述ZStack-2.5.1a 模板及無線接收移植相關內容。 main HAL_BOARD_INIT // HAL_TURN_OFF_LED1 InitBoard HalDriverInit HalAdcInit
兄弟連區塊鏈入門教程eth原始碼分析p2p-udp.go原始碼分析(二)
ping方法與pending的處理,之前談到了pending是等待一個reply。 這裡通過程式碼來分析是如何實現等待reply的。pending方法把pending結構體傳送給addpending. 然後等待訊息的處理和接收。 // ping sends a ping message to the giv
Spring原始碼分析(二)(IoC容器的實現)(1)
Ioc(Inversion of Control)——“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。理解好Ioc的關鍵是要明確“誰控制誰,控制什麼,為何是反轉(有
tornado原始碼分析(二)之iostream
在事件驅動模型中,所有任務都是以某個事件的回撥函式的方式新增至事件迴圈中的,如:HTTPServer要從socket中讀取客戶端傳送的request訊息,就必須將該socket新增至ioloop中,並設定回掉函式,在回掉函式中從socket中讀取資料,並且檢查request訊息是否全部接收到了,如果
Cat原始碼分析(二):Server端
初始化 服務端消費客戶端發來的訊息進行分析和展示,所以這個的初始化指的是CatHomeModule的初始化 CatHomeModule依賴TcpSocketReceiver和MessageConsumer,前者用來接收客戶端傳送的訊息,後者用來消費訊息。 TcpSocket
轉載:GCC原始碼分析(五)——指令生成
一、前言 又有好久沒寫了,的確很忙。前篇介紹了GCC的pass格局,它是GCC中間語言部分的核心架構,也是貫穿整個編譯流程的核心。在完成優化處理之後,GCC必須做的最後一步就是生成最後的編譯結果,通常情況下就是彙編檔案(文字或者二進位制並不重要)。 前面也講到了,
subsampling-scale-image-view載入長圖原始碼分析(二)
subsampling-scale-image-view原始碼分析概要分析總結 概要 subsampling-scale-image-view是一個支援部分載入大圖長圖的圖片庫,並且還支援縮放,在subsampling-scale-image-view載入長圖原
Spring component-scan原始碼分析(二) -- @Configuration註解處理
上篇文章Spring component-scan原始碼分析(一) – XML解析分析了Spring解析<context:component-scan …/>標籤時,把掃描到的合適的類封裝成BeanDefinition加入Sping容器中,本篇分析S
Spring原始碼分析(二)(IoC容器的實現)(3)
BeanDefinition的載入和解析 這個載入過程,相當於把定義的BeanDefinition在IoC容器中轉化成一個Spring內部表示的資料結構的過程。IoC容器對Bean的管理和依賴注入功能的實現,是通過對其持有的BeanDefinition進
Spring原始碼分析(二)(IoC容器的實現)(2)
IoC容器的初始化過程 簡單來說IoC容器的初始化是由refresh()方法啟動的,這個方法標誌著IoC容器的正式啟動。這個啟動包括BeanDefinition的Resouce定位、載入和註冊三個基本過程。 第一
groupcache 原始碼分析(二)-- LRU
lru部分的程式碼在lru/lru.go檔案中,它主要是封裝了一系列lru演算法相關的介面,供groupcahe進行快取置換相關的呼叫。 它主要封裝了下面幾個介面: // 建立一個Cache func New(maxEntries int) *Cache /
Spark2.3.2原始碼解析: 7. SparkContext原始碼分析(二) :TaskScheduler
程式碼部分: 啟動指令碼 --name spark-test --class WordCount --master yarn --deploy-mode cluster /A/spark-test.jar /