編譯程序的邏輯結構
編譯程序邏輯結構上至少包含兩大階段
-
分析(Analysis)階段
理解源程序,挖掘源程序的語義
-
綜合(Synthesis)階段
生成與源程序語義上等價的目標程序
編譯程序的前端、中端和后端
-
前端(Front End)
實現主要的分析任務
通常以第一次生成中間代碼為標志
-
后端(Back End),與目標機相關
實現主要的綜合任務(目標代碼生成和優化)
通常以從最后一級中間代碼生成目標代碼為標志
-
中端(Middle End)
實現各級中間代碼上的操作(中間代碼生成與優化)
典型編譯程序的邏輯過程
詞法分析
詞法分析程序(Lexical Analyzer)或詞法掃描程序(Scanner)的作用
-
從左至右掃描構成源程序的字符流
-
識別出有詞法意義的單詞(Lexemes)
-
返回單詞記錄(由單詞記號(Token)和單詞的屬性值組成),或詞法錯誤信息
-
除以上主要任務外,常伴有如下任務
濾掉空格,跳過注釋、換行符,追蹤換行標志,復制出錯源程序,宏展開,……也可能包含訪問符號表的操作
語法分析
判斷w是否是文法G的語言,如果是則給出語法分析樹。
語法分析過程主導語義分析以及翻譯的過程
語義分析
對語法分析后的程序進行語義分析,不符合語義規則時給出語義錯誤信息
符號表
收集每個名字的各種屬性用於語義分析及后續各階段
出錯處理
-
檢查錯誤
報告出錯信息(error reporting)
-
排錯
恢復編譯工作(error recovery)
中間代碼生成
抽象語法樹 AST
三地址碼 TAC
目標代碼生成
生成目標機代碼
小結: 典型編譯程序的主要邏輯模塊
文法基礎
1. 最左(右)推導
最左推導
最右推導
2. 0~3型文法分類及其包含關系
文法與語言的 Chomsky 分類方法
0 型文法
每個產生式左邊至少有一個非終結符
1型文法(上下文有關文法)
每個產生式右邊長度都 大於等於 左邊長度(除S→ε),且S不出現在右部
2型文法(上下文無關文法)
產生式左邊只有一個非終結符
3型文法(正規文法)
產生式左邊僅是一個非終結符
產生式右邊只由1個終結符(或ε)后面跟0到1個非終結符
能夠用3型文法定義的語言稱為3型語言或正規語言
文法之間的關系
3. 畫出語法分析樹
語法分析樹的果實
歸約、推導與分析樹之間關系
4. 文法的二義性
二義文法(ambiguous grammars)舉例
二義文法概念
二義性的判定
消除二義性
正規式、正規文法、自動機
1. DFA 和 NFA 的等價性
定理: L 是某個 DFA 的語言, 當且僅當 L 也是某個 NFA 的語言.
ε - 閉包
個人理解:ε_CLOSURE(I)表示從 集合I 中的每一個元素出發經過 ε邊能夠到達的點(包括集合I)。
move函數
個人理解:狀態集合 I 中的元素經過a邊可以到達的點的集合
子集構造法
NFA\(\Rightarrow\)DFA
子集構造法之例
2. DFA 的化簡:通過合並等價的(或不可區別的)狀態
DFA 狀態集合上的一個等價關系定義
DFA 狀態集合上等價關系的另一種解釋
無關狀態
無用狀態:從開始狀態不能到達的狀態
死狀態:不能到達終止狀態的狀態
DFA的簡化算法
3. 正規表達式
正規表達式算符優先級
舉例
4. 正規式和正規文法的等價
正規式轉換為正規文法
tips:未在考試重點
個人理解:第二條可以簡化成如下
舉例
正規文法轉換為正規式
5. 正規式與有窮自動機的等價
從正規表達式構造等價的ε - NFA
典型正規表達式對應的狀態轉換圖
從有限自動機M構造等價的正規表達式r
舉例
6. 正規文法與構造有窮自動機的等價
有窮自動機M\(\Rightarrow\)正規文法G
正規文法G構造有窮自動機M
LL(1)自頂向下
1. 消除(直接)左遞歸
tips:間接左遞歸不在考點
左遞歸消除規則
舉例
2. 提取左公因子
提取左公因子規則
舉例
3. Select集合
First 集合
個人理解:經過任意次推導得到的句型中的第一個終結符
計算 First 集合
個人理解:
- 終結符或ε的first集合為自己
- 產生式右部為空,即\(A \rightarrow \varepsilon\) First(A)並上\(\varepsilon\)
- 產生式右部非空,則First(左部)需並上First(右部)
- First(\(Y_1Y_2...Y_k\))。找到第一個First集合不包含\(\varepsilon\)的元素\(Y_i\)。將前面所有元素的First集合並起來,減去\(\{\varepsilon\}\)。如果不存在則所有元素的First集合並起來
Follow 集合(后繼符號集)
個人理解:Follow(A)求A后面可能字符串的First集合
計算 Follow 集合
個人理解:如果\(\beta\)為\(\varepsilon\),即A后面無內容,則Follow(A)需並上Follow(X)
Select集(預測集合)(Predictive Set)
4.判斷是否為LL(1)文法
舉例
5. 畫出預測分析表
表驅動 LL(1)分析程序
工作原理:借助於預測分析表和一個下推棧
初始時,下推棧只包含#;首先將文法開始符號入棧;之后依如下步驟:
-
若棧頂為終結符,則判斷當前讀入的單詞是否與該終結符相匹配,若匹配,再讀取下一單詞繼續分析;不匹配,則進行出錯處理
-
若棧頂為非終結符,則根據該非終結符和當前輸入單詞查預測分析表,若相應表項中是產生式(唯一的),則將此非終結符出棧,並把產生式右部符號從右至左入棧;若表項為空,則進行出錯處理
-
重復(1)和(2),直到棧頂為 # 同時輸入也遇到結束符 # 時,分析結束
預測分析表
-
表驅動分析程序需要的二維表M
-
表的每一行A 對應一個非終結符
-
表的每一列a對應某個終結符或輸入結束符 #
-
表中的項 M(A,a) 表示棧頂為A,下一個輸入符號為a時,可選的產生式集合
-
對於LL(1)文法,可以構造出一個 M(A,a) 最 多只包含一個產生式的預測分析表,可稱之為LL(1)分析表
-
M(A,a) 不含產生式時,對應一個出錯位置
預測分析表的構造算法
舉例
6. 對輸入塊的分析過程
LR(0)
1. 增廣文法
增廣文法:使開始符號不會出現在產生式的右部
2. 畫出活前綴的DFA
活前綴
活前綴與句柄的關系:一個活前綴是某一右句型的前綴,它不超過該右句型的某個句柄
-
活前綴已含有該句柄的全部符號,表明該句柄對應的產生式 A→α的右部α已出現在棧頂
-
活前綴只含該句柄的一部分符號,表明該句柄對應的產生式 A→α1α2 的右部子串α1 已出現在棧頂,期待從輸入串中看到α2 推導出的符號串
-
活前綴不含有該句柄的任何符號,此時期待從輸入串中看到該句柄對應的產生式 A→α的右部所推導出的符號串
活前綴集合的歸納定義:
LR(0)FSM 的狀態
LR(0)FSM 的狀態是一個特殊的 LR(0)項目(item)集
一個LR(0)項目是在右端某一位置有圓點的產生式
LR(0)項目解析
計算LR(0)項目集 I 的閉包 CLOSURE(I)的算法
圓點在產生式右部字符串的某個非終結符前,且該非終結符可以進行推導,則圓點也在該終結符推導出的式子的前面
LR(0)FSM 的初態
例子
LR(0)FSM 的狀態轉移函數
計算 LR(0)FSM 的所有狀態的集合
LR(0)FSM 的構造說明
同一狀態的項目集中,若不同項目其后繼符號相同時, 后繼狀態也相同
不同狀態的項目集中,若出現相同的項目時,則后繼狀態也相同. (如后圖狀態I2和I4同有項目A→.cA故經c均到達狀態I2 ,狀態I2和I4也同有項目A→.d故經d均到達狀態I10 )
LR(0)FSM 的構造舉例
LR(0)FSM 的語言
3. 判斷是否為LR(0)
LR(0)文法判斷
4. action表,goto表
LR分析表
ACTION表
告訴分析引擎:在棧頂狀態為k, 當前輸入符號是 a 時做什么
ACTION [k,a]=si | Shift:狀態 i 移進狀態棧頂,a移進符號棧頂 |
ACTION [k,a]=rj | Reduce:按第 j 條產生式A→β歸約,兩棧頂各彈出|β|個元素 |
ACTION [k,a]=acc | Accept :分析完成 |
ACTION [k,a]=err | Error :發現錯誤 (常標為空白) |
GOTO表
GOTO[i,A]=j 告訴分析引擎:在依產生式 A→β 歸約依Action完成動作后(兩棧頂各彈出|β|個元素),棧頂狀態為i 時,要將新狀態 j 移進狀態棧頂(同時: A 移入符號棧頂)
即:在棧頂狀態為k, 當前輸入符號是 A時做什么。GOTO表就一個shift操作
LR(0)分析表的構造
LR(0)分析表的構造舉例
5. 對輸入串的分析過程
LR分析
帶符號棧的 LR 分析算法
SLR(1)
LR(0)分析的局限性
發現兩個狀態(項目集)存在移進-歸約沖突
向前查看一個符號可解決沖突
SLR(1)分析思想
- 向前查看一個符號來改進對LR(0)狀態(項目集)中移進-歸約和歸約-歸約沖突的解決
- 根據下一個輸入符號是否屬於要歸約的非終結符的Follow 集來決定是否進行歸約
- 如果LR(0)狀態(項目集)中的所有歸約項中要歸約的非終結符的 Follow 集互不相交,則可以解決歸約-歸約沖突
- 如果LR(0)狀態(項目集)中的所有歸約項中要歸約的非終結符的 Follow 集與所有移進項目要移進的符號集互不相交,則可以解決移進-歸約沖突
- SLR(0)分析表的構造也基於LR(0)FSM
- 只需對 LR(0)分析表進行簡單修改使得歸約表項只適用於相應非終結符Follow 集中的輸入符號
SLR(1)分析表的構造
SLR(1)文法
比較 LR(0)表和 SLR(1)表
在 LR(0)表的 ACTION 表中,歸約表項總是整行出現的,即一個歸約對於所有輸入符號都適用; 不會既有移進又有歸約
而在 SLR(1)表的ACTION 表中。歸約表項只適用於相應非終結符Follow 集中的輸入符號; 可以既有移進又有歸約
LR(1)
LR(1)項目
LR(1)FSM
LR(1)FSM的構造
LR(1)FSM的初態
LR(1)FSM的狀態轉移函數
計算 LR(1)項目集規范族
LR(1)FSM的構造舉例
LR(1)分析表的構造
LR(1)分析表的構造舉例
LR(1)文法
語義分析、中間代碼
0. 語法制導的語義計算基礎(不在考點,前導概念)
概念
-
屬性文法(Attribute Grammar)在上下文無關文法的基礎上進行如下擴展
- 為每個文法符號關聯多個屬性(Attribute)
- 為文法的每個產生式關聯一個語義規則集合或稱為語義動作。
-
屬性
- 屬性(Attribute)可用來刻畫一個文法符號的任何我們所關心的特性,如:符號的值,符號的名字串,符號的類型,符號的偏移地址,符號被賦予的寄存器,代碼片斷,等等…
-
記號
- 文法符號 X 關聯屬性 a 的屬性值可通過 X.a 訪問
-
語義規則(Semantic Rule)
- 在屬性文法中,每個產生式 \(A \rightarrow \alpha\) 都關聯一個語義規則的集合,用於描述如何計算當前產生式中文法符號的屬性值或附加的語義動作
-
屬性文法中允許如下語義規則
- 復寫(copy)規則,形如X.a := Y.b
- 基於語義函數(semantic function)的規則,形如
b:=f(c1, c2, …, ck) 或 f(c1, c2, …, ck)
其中,b,c1, c2, …, ck是該產生式中文法符號的屬性
-
綜合屬性(synthesized attribute)
-
用於“自下而上”傳遞信息
- 對關聯於產生式 \(A \rightarrow \alpha\) 的語義規則 b:=f(c1, c2, …, ck) ,如果 b 是 A 的某個屬性, 則稱 b 是 A 的一個綜合屬性
-
綜合屬性的例子
-
-
繼承屬性(inherited attribute)
-
用於“自上而下”
-
傳遞信息對關聯於產生式$$A \rightarrow \alpha$$ 的語義規則 b:=f(c1, c2, …, ck) ,如果 b 是產生式右部某個文法符號 X 的某個屬性,則稱 b 是文法符號 X 的一個繼承屬性
-
繼承屬性的例子
繼承屬性代表自上而下傳遞的信息
-
基於屬性文法的語義計算
S-屬性文法
- 只包含綜合屬性
- 可以自下而上計算分析樹中節點的屬性
L-屬性文法
L-屬性文法的語義計算
-
采用自上而下的方式可以較方便地進行
-
可以采用下列基於深度優先后序遍歷的算法
-
該算法與自上而下預測分析過程對應. 因此,基於 LL(1) 文法的 L-屬性文法可以采用這種方法進行語義計算.隨后將結合翻譯模式的進一步討論分析程序的構造)
采用基於深度優先后序遍歷算法進行 L-屬性文法的語義計算舉例
翻譯模式(Translation Scheme)概念
- 適合語法制導語義計算的另一種描述形式
- 可以體現一種合理調用語義動作的翻譯算法
- 形式上類似於屬性文法,但允許由{ }括起來的語義規則集合出現在產生式右端的任何位置. 這樣做的好處是可以顯式地表達動作和屬性計算的次序,而在前述的屬性文法中不體現這種次序
受限的翻譯模式
- 在設計翻譯模式時,必須作某些限制,以確保每個屬性值在被訪問到的時候已經存在
- 本講僅討論兩類受限的翻譯模式
- S-翻譯模式,為僅需要綜合屬性的情形,創建一個語義規則集合,放在相應產生式右端的末尾,把屬性的計算規則加入其中即可。該模式類似與S-屬性文法,不重復討論。
- L-翻譯模式,對於既包含繼承屬性又包含綜合屬性的情形,但需要滿足:
- (1)產生式右端某個符號繼承屬性的計算必須位於該符號之前,其語義動作不訪問位於它右邊符號的屬性,只依賴於該符號左邊符號的屬性(對於產生式左部的符號,只能是繼承屬性);
- (2)產生式左部非終結符的綜合屬性的計算只能在所用到的屬性都已計算出來之后進行,通常將相應的語義動作置於產生式的尾部。
翻譯模式舉例
定點二進制小數轉換為十進制小數
1. 聲明語句的屬性文法
類型表達式(type expressions)
由基本類型,類型名字,類型變量,及類型構造子 (type constructor)歸納定義的表達式
分四類定義
-
基本數據類型表達式
-
純量類型表達式:bool, int, real
-
有界數組類型表達式:array(I,T)
T∈{ bool, int, real };I 代表一個整數區間,如 1..10
-
指針數據類型表達式:pointer(T)
T∈
-
-
積類型表達式
-
<T1, T2, …, Tn>
T1, T2, …, Tn 為上述數據類型表達式;若n=0,則表示為 < >
-
-
過程類型表達式
- fun(T)T 是上述積類型表達式
-
專用類型表達式
- type_error 專用於有類型錯誤的程序單元
- ok 專用於沒有類型錯誤的程序單元
語義函數
make_product_3 (V1.type, T.type, L.num) | 生成積類型表達式< t1,…,tm,T.type,…,T.type>T.type共L.num個,V1.type=<t1,…,tm> 。 |
num.lexval | 為詞法分析返回的單詞屬性值 |
id.entry | 指向當前標識符對應於符號表中的表項 |
addtype(id.entry,L.in) | 表示將屬性值 L.in 填入當前標識符在符號表表項中的type 域( 記錄標識符的類型) |
id.name | 為當前標識符的名字; |
lookup_type(id.name) | 從符號表中查找名字為 id.name 的標識符所對應的表項中 type 域的內容,若未查到該表項或表項中的 type 域無定義,則返回 nil。 |
type_error | 專用於有類型錯誤的程序單元 |
ok | 專用於沒有類型錯誤的程序單元 |
match( fun (type1), type2) | 返回 true,當且僅當 type1, type2 是完全相同的積類型表達式 |
id.place | id 對應的存儲位置 |
E.place | 用來存放 E 的值的存儲位置 |
E.code | E 求值的 TAC 語句序列(可不止一條四元式) |
S.code | 對應於 S 的 TAC 語句序列 |
newtemp | 在符號表中新建一個名字,返回該名字存儲位置 |
newlabel | 返回一個新的語句標號 |
gen | 生成一條 TAC 語句 |
|| | TAC 語句序列之間的鏈接運算 |
類型檢查程序的設計
語法制導的方法
- 將類型表達式作為屬性值賦給程序各個部分
- 設計恰當的翻譯模式
- 可實現相應語言的一個類型系統
語法制導的類型檢查程序 —— 舉例
處理聲明的翻譯模式
處理表達式的翻譯模式(不在考點)
處理語句、過程聲明及程序的翻譯模式(不在考點)
2. 寫出四元式序列(布爾表達式,短路求值)
布爾表達式的語法制導翻譯
直接對布爾表達式求值
翻譯流程控制布爾表達式至短路代碼(L-翻譯模式)
語法分析樹
通過控制流體現布爾表達式的語義
運行時存儲組織
1. 棧區、活動記錄、靜態鏈和動態鏈
棧式存儲分配
- 用於有效實現可動態嵌套的程序結構
- 如實現過程/函數,遞歸,塊層次結構
- 比較:靜態分配不宜實現遞歸過程/函數
- 運行的時候才能確定數據對象的存儲分配結果
- 過程/函數的實現中,運行棧中的數據單元是活動記錄(activation record,后面專門討論)
- 運行時每當進入一個過程/函數,就在棧頂為該過程/函數分配存放活動記錄的數據空間;工作完畢返回時,棧頂的活動記錄數據空間隨即釋放
- 在過程/函數的某一次執行中,其活動記錄中會存放生存期在該過程/函數本次執行中的數據對象,以及必要的控制信息單元。
- 在編譯期間, 過程/ 函數以及嵌套程序塊的活動記錄大小(最大值) 應該是可以確定的,如果不滿足則應該使用堆式存儲管理
活動記錄
函數/過程調用或返回時,在運行棧上創建或從運行棧上消去的棧幀(frame)
包含局部變量,函數實參,臨時值(用於表達式計算的中間單元)等數據信息以及必要的控制信息
過程活動記錄的棧式分配舉例
典型的過程活動記錄結構
活動過程記錄舉例
靜態鏈
Display 表的方法要用到多個存儲單元或多個寄存器。
一種可選的方法是采用靜態鏈,也稱訪問鏈、存取鏈
所有活動記錄都增加一個靜態鏈(如在offset 為 0 處)的域,指向定義該過程的直接外過程(或主程序)運行時最新的活動記錄
過程返回時, 當前活動記錄要被撤銷,為回( unwind)到調用過程的活動記錄(恢復FP),需要在被調用過程的活動記錄中有這樣一個域,即動態鏈,指向該調用過程的活動記錄(的基址),也稱控制鏈
個人理解:靜態鏈指向定義該過程的外過程,動態鏈指向調用該過程的外過程
靜態鏈動態鏈舉例
代碼生成
1. 支配節點集、回邊、自然循環
基本塊(basic block)
概念
- 程序中一個順序執行的語句序列
- 只有一個入口語句和一個出口語句
- 除入口語句外其他語句均不可以帶標號
- 除出口語句外其他語句均不可能是轉移或停語句
入口語句
- 程序的第一個語句
- 條件轉移語句或無條件轉移語句的轉移目標語句
- 緊跟在條件轉移語句后面的語句
划分基本塊的算法
-
針對三地址碼(TAC)
-
步驟
- 出 TAC 程序之中各個基本塊的入口語句
- 對每一入口語句,構造其所屬的基本塊。它是由該語句到下一入口語句(不包括下一入口語句),或到一轉移語句(包括該轉移語句),或到一停語句(包括該停語句)之間的語句序列組成的
- 凡未被納入某一基本塊的語句,都是程序中控制流程無法到達的語句,因而也是不會被執行到的語句,可以把它們刪除
-
舉例
流圖(flow graph)
概念
- 可以為構成程序的基本塊增加控制流信息,方法是構造一個有向圖,稱之為流圖或控制流圖(CFG,Control-Flow Graph)
流圖以基本塊集為結點集;第一個結點為含有程序第一條語句的基本塊;從基本塊 i 到基本塊 j 之間存在有向邊當且僅當
- 基本塊 j 在程序的位置緊跟在 i 后,且 i 的出口語句不是轉移 (可為條件轉移)語句、停語句或者返回語句;
- 或者i 的出口是 goto(S) 或 if …goto(S), 而 (S) 是 j 的入口語句
舉例
循環(loop)
支配結點集(dominators)
如果從流圖的首結點出發,到達 n 的任意通路都要經過 m,則稱 m 支配 n, 或 m 是 n 的支配結點,記為 m DOM n(∀a. a DOM a)
結點n 的所有支配結點的集合, 稱為結點 n 的支配結點集,記為D(n)
注意:支配結點也稱必經節點,支配節點集也稱必經節點集
支配結點集舉例
自然循環(natural loop)
假設 n→d 是流圖中的一條有向邊,如果 d DOM n 則稱 n→d 是流圖中的一條回邊(back edge)
有向邊 n→d 是回邊,它對應的自然循環是由結點 d ,結點 n 以及有通路到達 n 而該通路不經過 d 的所有結點組成,並且 d 是該循環的唯一入口結點
同時,因 d 是 n 的支配結點,所以 d 必可達該循環中任意結點
注:流圖中的任何結點都是從首結點可達的
自然循環舉例