線性資料結構
語言處理器
簡單地說,一個編譯器就是一個程式,它可以閱讀以某一種語言(源語言)編寫的程式,並把該程式編譯成為一個等價的、用另一中語言(目標語言)編寫的程式。
直譯器:它並不通過翻譯的方式生成目標程式,而是直譯器直接利用使用者提供輸入執行源程式中的指定操作。
對比:一個編譯器產生的機器語言目標程式比一個直譯器快很多。然而,直譯器的錯誤診斷效果要比編譯器更好,因為它逐個語句地執行源程式。
Java語言處理器
Java語言處理器結合了編譯和解釋過程。一個Java源程式首先被編譯成一個稱為位元組碼(bytecode)的中間表示形式。然後由一個虛擬機器對得到的位元組碼加以解釋執行。這樣的好處時一臺機器上編譯得到的位元組碼可以在另一臺機器上解釋執行。
前處理器程式
一個源程式可能被分割成為多個模組,並存放於獨立的檔案中。把源程式聚合在一起的任務有時會被前處理器的程式獨立完成。
功能:把那些成為巨集的縮寫形式轉換為源語言的語句。
連結器
功能:能夠解決外部記憶體地址的問題。
載入器
功能:把所有的可執行目標檔案放到記憶體中執行
一個編譯器的結構
編譯器能夠把源程式對映為在語義上等價的目標程式。由兩個部分組成:分析部分和綜合部分。
分析部分(前端)
把源程式分解成為多個組成要素,並在這些要素之上加上語法結構。它使用這個結構來建立該源程式的一箇中間表示。如果分析部分檢查出源程式沒有按照正確的語法構成,或者語義上不一致,它就必須提供有用的資訊。分析部分還會收集有關源程式的資訊,並把資訊存放在一個符號表
綜合部分(後端)
根據中間表示和符號表中的資訊來構造使用者期待的目標程式。
詞法分析
詞法分析器讀入組成源程式的字元流,並將它們組織成為有意義的詞素(lexeme)的序列。對於每個詞素,詞法分析器產生如下形式的詞法單元(token)作為輸入:
<token-name,attribute-value>
詞法單元被傳送給下一個步驟,即語法分析。 token-name是一個由語法分析步驟使用的抽象符號, attribute-value指向符號表關於這個詞法單元的條目。
符號表條目的資訊會被語義分析和程式碼生成步驟使用。
一個例子
position = initial + rate *60
這些詞法單元將被傳遞給語法分析階段
1)position是一個詞素,被對映成 詞法單元<id
2)賦值符號= 是一個詞素,被對映成詞法單元<=>。因為這個詞法單元不需要屬性值,所以我們省略了第二個分量。也可以使用assign這樣的抽象符號作為詞法單元的名字,但是為了標記上的方便,我們選擇使用詞素本身作為抽象符號的名字。
3)initial是一個詞素,被對映成詞法單元<id,2>,其中2指向initial對應的符號表條目。
4)+是一個詞素,被對映成詞法單元<+>
5)rate是一個詞素,被對映成詞法單元<id,3>,其中3指向rate對應的符號表條目。
6)是一個詞素,被對映成<>。
7)60是一個詞素,被對映成詞法單元<60>
分隔詞素的空格會被詞法分析器忽略掉。
詞法單元序列
<id,1> = <id,2><+><id,3><*><60>
語法分析
語法分析器使用由詞法分析器生成的各個詞法單元的第一個分量來建立樹形的中間表示。該中間表示給出了詞法分析產生的詞法單元流的語法結構。一個常用的表示方法是語法樹(systax tree),樹中的每個結點表示一個運算,而該結點的子節點表示該運算的分量。
語義分析
語義分析器(semantic analyzer)使用語法樹和符號表中的資訊來檢查源程式是否和語言定義的語義一致。它同時也收集型別資訊,並把這些資訊存放在語法樹或符號表中,以便在隨後的中間程式碼生成過程中使用
語義分析的一個重要部分是型別檢查(type checking)。編譯器檢查每個運算子是否具有匹配的運算分量。比如,很多程式設計語言的定義中要求一個數組的下標必須是整數。如果用一個浮點數作為陣列下標,編譯器就必須報告錯誤。
中間程式碼生成
一個編譯器可能構造出一個或多箇中間表示。這些中間表示可以有多種形式。語法樹是一種中間表示形式,它們通常在語法分析和語義分析中使用。
三地址程式碼的中間表示形式。這種中間表示由一組類似於組合語言的指令組成,每個指令具有三個運算分量。每個運算分量都像一個暫存器。
三地址賦值指令的右部最多隻有一個運算子。因此這些指令確定了運算完成的順序。
程式碼優化
那些優化工作做得最多的編譯器,即所謂的"優化編譯器",會在優化階段花相當多的時間。有些簡單的優化方法可以極大地提高目標程式的執行效率而不會過多降低編譯的速度。
程式碼生成
程式碼生成器以源程式的中間表示形式作為輸入,並把它對映到目標語言。如果目標語言是機器程式碼,那麼就必須為程式使用的每個變數選擇暫存器或記憶體位置。然後,中間指令被翻譯成能夠完成相同任務的機器指令序列。程式碼生成的一個至關重要的方面是合理分配暫存器以存放變數的值。
符號表管理
編譯器的重要功能之一是記錄源程式中使用變數的名字,並收集和每個名字的各種屬性有關的資訊。這些屬性可以提供一個名字的儲存分配、它的型別、作用域(即在程式哪些地方可以使用這個名字的值)等資訊。對於過程名字,這些資訊還包括:它的 引數數量和型別、每個引數的傳遞方法(比如傳值或傳引用)以及返回型別。
前端步驟
詞法分析、語法分析、語義分析以及中間程式碼生成可以被組合在一起成為一趟。程式碼優化可以作為一個可選的趟。然後可以有一個為特定目標機生成程式碼的後端趟。
編譯器構造工具
-
語法分析器的生成器:可以根據一個程式設計語言的語法描述自動生成語法分析器。
-
掃描器的生成器:可以根據一個語言的語法單元的正則表示式描述生成詞法分析器。
-
語法制導的翻譯引擎;可以生成一組用於遍歷分析樹並生成中間程式碼的例程。
-
程式碼生成器的生成器:依據一組關於如何把中間語言的每個運算翻譯成為目標機上的機器語言的規則,生成一個 程式碼生成器。
-
資料流分析引擎:可以幫助收集資料流資訊,即程式中的值如何從程式的一個部分傳遞到另一個部分。資料流分析是程式碼優化的一個重要部分。
-
編譯器構造工具集:提供了可用於構造編譯器的不同階段的例程的完整集合。
強制式語言和宣告式語言
如何完成一個計算任務的語言稱為強制式語言(imperative)
程式中指明要進行哪些計算的語言稱為宣告式語言(declarative)
面向物件的主要思想:
- 資料抽象
- 特性的繼承
人們發現這兩者都可以使得程式更加模組化和易於維護。面向物件程式和用很多其他語言編寫的程式之間的不同在於它們由多得多得(但是較小)過程(在面向物件術語中稱為方法(method))組成。因此,編譯器優化技術必須能夠很好地跨越源程式中得過程邊界進行優化
計算機體系結構的優化
幾乎所有的高效能系統都利用了兩種技術:並行(parallelism)和記憶體層次結構(memory hierarchy)
並行可以出現在多個層次上
在指令層次上多個運算可以被同時執行;
在處理器層次上,同一個應用的多個不同執行緒在不同的處理器上執行
記憶體層次結構是應對下述侷限性的方法:
並行性
所有的現代微處理器採用指令級並行性。硬體動態地檢測順序指令流之間的依賴關係,並且在可能的時候並行地發出指令。在有些情況下,機器包含一個硬體排程器。該排程器可以改變指令的順序以提高程式的並行性。不管硬體是否對指令進行重新排列,編譯器都可以重新安排指令,以使得指令級並行更加有效。
名字、識別符號和變數
雖然 "名字" 和 "變數"通常指的是同一個事物,但注意的是編譯時刻的名字和名字在執行時刻所指的記憶體位置有所區分。
識別符號(identifier)是一個字串,通常由字母和數字組成。它用來指向(標記)一個實體,比如一個數據物件、過程、類、型別。所有的識別符號都是名字,但並不是所有的名字都是識別符號。
過程、函式和方法
過程:一個可以被呼叫的子程式。
函式:C語言
方法:Java