1. 程式人生 > >GCC原始碼分析(二)——前端

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
  1.   switch (c)  
  2.     {  
  3.     case ' 'case '\t'case '\f'case '\v'case '\0':  
  4.       result->flags |= PREV_WHITE;  
  5.       skip_whitespace (pfile, c);  
  6.       goto skipped_white;  
  7.   
  8.     case '\n':  
  9.       if (buffer->cur < buffer->rlimit)  
  10.     CPP_INCREMENT_LINE (pfile, 0);  
  11.       buffer->need_line = true;  
  12.       goto fresh_line;  
  13.   
  14.     case '0'case '1'case '2'case '3'case '4':  
  15.     case '5'case '6'case '7'case '8'case '9':  
  16.       {  
  17.     struct normalize_state nst = INITIAL_NORMALIZE_STATE;  
  18.     result->type = CPP_NUMBER;  
  19.     lex_number (pfile, &result->val.str, &nst);  
  20.     warn_about_normalization (pfile, result, &nst);  
  21.     break;  
  22.       }  
  23.   
  24.     case 'L':  
  25.     case 'u':  
  26.     case 'U':  
  27.     case 'R':  
  28.       /* 'L', 'u', 'U', 'u8' or 'R' may introduce wide characters, 
  29.      wide strings or raw strings.  */  
  30.       if (c == 'L' || CPP_OPTION (pfile, uliterals))  
  31.     {  
  32.       if ((*buffer->cur == '\'' && c != 'R')  
  33.           || *buffer->cur == '"'  
  34.           || (*buffer->cur == 'R'  
  35.           && c != 'R'  
  36.           && buffer->cur[1] == '"'  
  37.           && CPP_OPTION (pfile, uliterals))  
  38.           || (*buffer->cur == '8'  
  39.           && c == 'u'  
  40.           && (buffer->cur[1] == '"'  
  41.               || (buffer->cur[1] == 'R' && buffer->cur[2] == '"'))))  
  42.         {  
  43.           lex_string (pfile, result, buffer->cur - 1);  
  44.           break;  
  45.         }  
  46.     }  
  47.       /* Fall through.  */  
  48.   
  49.     case '_':  
  50.     case 'a'case 'b'case 'c'case 'd'case 'e'case 'f':  
  51.     case 'g'case 'h'case 'i'case 'j'case 'k'case 'l':  
  52.     case 'm'case 'n'case 'o'case 'p'case 'q'case 'r':  
  53.     case 's'case 't':           case 'v'case 'w'case 'x':  
  54.     case 'y'case 'z':  
  55.     case 'A'case 'B'case 'C'case 'D'case 'E'case 'F':  
  56.     case 'G'case 'H'case 'I'case 'J'case 'K':  
  57.     case 'M'case 'N'case 'O'case 'P'case 'Q':  
  58.     case 'S'case 'T':           case 'V'case 'W'case 'X':  
  59.     case 'Y'case 'Z':  
  60.       result->type = CPP_NAME;  
  61.       {  
  62.     struct normalize_state nst = INITIAL_NORMALIZE_STATE;  
  63.     result->val.node.node = lex_identifier (pfile, buffer->cur - 1, false,  
  64.                         &nst);  
  65.     warn_about_normalization (pfile, result, &nst);  
  66.       }  
  67.   
  68.       /* Convert named operators to their proper types.  */  
  69.       if (result->val.node.node->flags & NODE_OPERATOR)  
  70.     {  
  71.       result->flags |= NAMED_OP;  
  72.       result->type = (enum cpp_ttype) result->val.node.node->directive_index;  
  73.     }  
  74.       break;  
  75.   
  76.     case '\'':  
  77.     case '"':  
  78.       lex_string (pfile, result, buffer->cur - 1);  
  79.       break;  
  80.   
  81.     case '/':  
  82.       /* A potential block or line comment.  */  
  83.       comment_start = buffer->cur;  
  84.       c = *buffer->cur;  
  85.         
  86.       if (c == '*')  
  87.     {  
  88.       if (_cpp_skip_block_comment (pfile))  
  89.         cpp_error (pfile, CPP_DL_ERROR, "unterminated comment");  
  90.     }  
  91.       else if (c == '/' && (CPP_OPTION (pfile, cplusplus_comments)  
  92.                 || cpp_in_system_header (pfile)))  
  93.     {  
  94.       /* Warn about comments only if pedantically GNUC89, and not 
  95.          in system headers.  */  
  96.       if (CPP_OPTION (pfile, lang) == CLK_GNUC89 && CPP_PEDANTIC (pfile)  
  97.           && ! buffer->warned_cplusplus_comments)  
  98.         {  
  99.           cpp_error (pfile, CPP_DL_PEDWARN,  
  100.              "C++ style comments are not allowed in ISO C90");  
  101.           cpp_error (pfile, CPP_DL_PEDWARN,  
  102.              "(this will be reported only once per input file)");  
  103.           buffer->warned_cplusplus_comments = 1;  
  104.         }  
  105.   
  106.       if (skip_line_comment (pfile) && CPP_OPTION (pfile, warn_comments))  
  107.         cpp_error (pfile, CPP_DL_WARNING, "multi-line comment");  
  108.     }  
  109.       else if (c == '=')  
  110.     {  
  111.       buffer->cur++;  
  112.       result->type = CPP_DIV_EQ;  
  113.       break;  
  114.     }  
  115.       else  
  116.     {  
  117.       result->type = CPP_DIV;  
  118.       break;  
  119.     }  
  120.   
  121.       if (!pfile->state.save_comments)  
  122.     {  
  123.       result->flags |= PREV_WHITE;  
  124.       goto update_tokens_line;  
  125.     }  
  126.   
  127. 相關推薦

    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 /