1. 程式人生 > >編譯器原理學習筆記 一

編譯器原理學習筆記 一

語言處理器

Created with Raphaël 2.1.0源程式前處理器經過預處理的源程式編譯器目標彙編程式彙編器可重定位機器程式碼連結器/載入器目標機器程式碼
  • 編譯器 (compiler) : 將源語言翻譯成目標語言。重要任務是在翻譯過程中發現源語言中存在的錯誤。相比於直譯器,目標語言執行的速度快。
  • 直譯器 (interpreter) : 利用使用者的輸入執行源程式中指定的操作。相比於編譯器,錯誤診斷效果好,直譯器是逐個語句執行。
  • 前處理器 (preprocessor) : 把源程式聚合在一起,把巨集的縮寫轉換為源語言的語句
  • 彙編器 (assembler) : 將組合語言程式處理後生成可重定位的機器程式碼
  • 連結器 (linker) : 解決外部記憶體地址問題。將多個可重定位的目標檔案以及庫檔案連結到一起,形成真正在機器上執行的程式碼
  • 載入器 (loader) : 把所有的可執行目標檔案放到記憶體中執行

一個編譯器的結構

Created with Raphaël 2.1.0字元流詞法分析器符號流語法分析語法樹語義分析語法樹中間程式碼生成器中間表示形式機器無關程式碼優化器中間表示形式程式碼生成器目標機器語言機器相關程式碼優化器目標機器語言

編譯過程: 分析部分,綜合部分

  • 分析部分 (front end) : 檢查源程式的語法和語義,出現錯誤時提供有用的資訊;建立中間表示符號表 (symbol table)
    傳遞給綜合部分
  • 綜合部分 (back end) :根據中間表示和符號表來構建目標程式
    這裡寫圖片描述

詞法分析 (lexical analysis)

  • 詞法分析器:將源程式的字元流組織成為有意義的詞素 (lexeme) 序列。對於每個詞素,詞法分析器產生如下形式的詞法單元 (token) 作為輸出:
    <token-name, attribute-value>
    token-name: 語法分析步驟使用的抽象符號。
    attribute-value: 指向符號表中關於這個詞法單元的條目。一個識別符號的符號表條目存放該識別符號有關的資訊,例如名字和型別。

  • 舉例說明:
    position = initial + rate * 60


    分析如下:

詞素 詞法單元
position <id,1>
= <=>
initaial <id,2>
+ <+>
rate <id,3>
* <*>
60 <60>

語法分析 (syntax analysis)

語法分析器:使用詞法單元的第一個分量來建立樹形的中間表示,也就是根據抽象符號 (識別符號、運算子 …)來構建語法結構。一個常用的表示方法語法樹 (syntax tree),樹中的每個內部結點表示一個運算,該結點子結點表示運算的分量。

語法樹

語義分析 (semantic analysis)

  • 語義分析器 (semantic analyzer): 使用語法樹和符號表中的資訊來檢查是否和語言定義的語義一致,並將型別資訊存放在語法樹或符號表中。
  • 語義分析的一個重要部分是型別檢查 (type checking)。編譯器檢查到陣列下標是浮點數就會報錯;或者檢查到一個二元運算子應用於一個浮點數和一個整數,則會把整數轉換為一個浮點數,也就是自動型別轉換 (coercion)
    這裡寫圖片描述

中間程式碼生成

  1. 在語法分析和語義分析完成之後,很多編譯器會生成一個類機器語言的中間表示。我們可以把這個中間表示看作是某個抽象機器的程式。該中間表示具有兩個重要性質:易於生成;能夠被輕鬆地翻譯為目標機器上的語言
  2. 三地址程式碼 (three-address code): 類似於組合語言的指令組成,每個指令具有三個運算分量。三地址賦值指令右部最多隻有一個運算子;編譯器生成臨時名字存放一個三地址指令計算得到的值;有些三地址指令的運算分量少於三個

程式碼優化

  1. 機器無關的程式碼優化步驟檢視改進中間程式碼來生成更好的目的碼。”更好” 意味著更快, 更短,能耗更低。。。。的目的碼。
  2. 使用一個簡單的中間程式碼生成演算法,然後再進行程式碼優化步驟是生成優質目的碼的一個合理方法。

程式碼生成

  1. 程式碼生成器:將源程式的中間表示形式對映成目標語言。如果目標語言是機器程式碼,就必須為程式使用的每個變數選擇暫存器或記憶體位置,然後中間指令被翻譯成能夠完成相同任務的機器指令序列。
  2. 程式碼生成的一個至關重要的方面是合理分配暫存器以存放變數的值。

符號表管理

  1. 符號表資料結構:為每個變數名字建立一個記錄條目。變數名字:儲存分配,型別,作用域等。過程名字:儲存分配,型別,作用域,引數數量和型別,引數的傳遞方法,返回型別等等。。

將多個步驟組合成趟

  1. 前端趟:將詞法分析,語法分析,語義分析,中間程式碼生成組合在一起
  2. 程式碼優化作為一個可選的趟。
  3. 後端趟:為特定目標機生成程式碼。
  4. 不同的前端趟和某個目標機的後端趟結合起來,可以為不同的源語言建立該目標機上的編譯器;把一個前端趟和不同的目標機後端趟結合起來,建立針對不同目標機的編譯器。

編譯器構造工具

一些常用的編譯器構造工具:
1. 語法分析器的生成器:可以根據一個程式設計語言的語法描述自動生成語法分析器。
2. 掃描器的生成器:可以根據一個語言的語法單元的正則表示式描述生成詞法分析器。
3. 語法制導的翻譯引擎:可以生成一組用於遍歷分析數並生成中間程式碼的例程。
4. 程式碼生成器的生成器:依據一組關於如何把中間語言的每個運算翻譯成為目標機上的機器語言的規則生成一個程式碼生成器。
5. 資料流分析引擎:可以幫助收集資料流資訊,即程式中的值如何從程式的一個部分傳遞到另一個部分。資料流分析是程式碼優化的一個重要部分。
6. 編譯器構造工具集:提供了可用於構造編譯器的不同階段的例程的完整集合。

程式設計語言的發展歷程

第一臺電子計算機出現在 20 世紀 40 年代。

走向高階程式設計語言

  • 第一步是 20 世紀 50 年代早期人們對助記組合語言的開發。
  • 重大一步發生在 20 世紀 50 年代的後五年。設計高層次的表示方法,例如用於科學計算的 Fortran 語言,用於商業資料處理的 Cobol 語言和用於符號計算的 Lisp 語言被開發出來
  • 語言分類
    1. 根據語言的代:第一代是機器語言;第二代是組合語言;第三代是Fortran,Cobol,Lisp,C,C++,C# 及 Java 這樣的高階程式設計語言;第四代語言是為特定應用設計的語言,例如,用於生成報告的 NOMAD, 用於資料庫查詢的 SQL 和用於文字排版的 Postscript。第五代語言指的是基於邏輯和約束的語言,比如 Prolog 和 OPS5。
    2. 程式中指明如何完成一個計算任務的語言稱為強制式 (imperative) 語言,例如C,C++,C# 和 Java;程式中指明要進行哪些計算的語言稱為宣告式 (declarative) 語言,例如 ML,Haskell,Prolog。
    3. 馮 · 諾依曼語言 (von Neumann language) 是指以馮 · 諾依曼計算機體系結構為計算模型的程式設計語言,例如 Fortran 和 C;面嚮物件語言 (object-oriented language) 是指支援面向物件程式設計的語言,面向物件程式設計是指用一組相互作用的物件組成程式,Simula 67 和 Smalltalk 是早期的主流面嚮物件語言, C++,C#,Java 和 Ruby 是現在常用的面嚮物件語言;指令碼語言 (scripting language) 是指具有高層次運算子的解釋型語言,通常用於把多個計算過程粘合在一起,例如 Awk,JavaScript,Perl,PHP,Python,Ruby 和 Tcl。

構造一個編譯器的相關學科

  • 編譯器優化必須滿足:
    1. 優化必須是正確的,也就是說,不能改變被編譯程式的含義。
    2. 優化必須能夠改善很多程式的效能。
    3. 優化所需的時間必須保持在合理的範圍內。
    4. 所需要的工程方面的工作必須是可管理的。