編譯原理


步驟

  • 詞法分析
  • 語法分析
  • 語義分析與中間代碼產生
  • 優化
  • 目標代碼生成

文法

  • 3型文法:正則文法,用於描述程序設計語言詞法的有效工具
  • 2型文法:上下型無關文法,描述程序語法的有效工具

產生式

A -> B
B -> BC|C
C -> 0|1|2|3|4|5|6|7|8|9

推導與規約

A -> aBc
B -> b
==================
aBc是abc的歸約
abc是aBc的推導

規范推導/規約

規范推導:最右推導

規范規約:最左規約

無符號串
=》數字串
=》數字串+數字
=》數字串6
=》數字6
=》56

句型

由產生式到最終狀態之間的中間串

例如<數字>9

句子

產生式最終匹配的終態

例如56是文法無符號整數的一個句子

詞法分析

有限自動機

DFA

確定有限自動機:每個狀態在接收下一個輸入,只會轉移到唯一一個下一個狀態

complier-dfa
L(M):代表由FA能夠識別的所有字符串的總體

NFA

非確定有限自動機,自動機中含有某些狀態,在接收下一個輸入后,會轉移到多於一個的狀態

complier-nfa

NFA確定化

每個NFA都存在一個DFA,使得L(NFA) = L(DFA),也就是說等價

NFA的確定化就是,從NFA轉變為DFA的過程

  • c-closure(state):c-閉包,從一個狀態轉移不需要通過輸入可以直接轉移到下一個狀態的集合
  • move(state, action):從狀態state,通過action,轉換到的下一個狀態的集合
  • a弧轉換的閉包:I = c-closure(move(state, action))

子集構造法

complier-nfa2dfa
然后把I列表標記為狀態,並畫出DFA

在NFA->DFA后,得到的DFA並不是最簡的,必須通過DFA最簡化處理,來提高分析的效率

DFA最簡化
  • 多余狀態:從開始狀態出發無法到達的狀態
  • 死狀態:從此狀態出發無法到達最終狀態的狀態
  • 無關狀態:多余狀態+死狀態
  • 等價狀態(不可區別狀態):當兩個狀態,輸入任意action,都轉移到相同的狀態時,代表這兩個狀態是等價的。

簡化步驟為:

  • 消除無關狀態
  • 合並等價狀態
正則與NFA的轉換
R->NFA

complier-r2nfa
例子

complier-r2nfa-sample

NFA-R

逆過程

語法分析

自頂向下語法分析

消除左遞歸
直接左遞歸
P->Pa|b => ba+
	P ->bM
	M -> aM|空
間接左遞歸
A -> Bc|d
B -> aA|Ab
=>
	A -> aAc|Abc|d
	=> 
		A -> (aAc|d)M
		M -> bcM|空
First集

若X->a....則a加入X的First集中,如果X->空,則空加入到X的First集中

X->amm|bk|空
	=> First(X) = {a,b,空}
X->Ym|b
Y->a|d|空
	=> First(X) = {a, d, m, b}
Follow集
  • 若A->aBC,則First(C)加入Follow(B)
  • 若A->aB,則Follow(A)加入Follow(B)
LL(1)語法分析

complier-ll1
L:從左向由掃描

L:最左推導

1:向前看1個字符

對文法G的句子進行確定的自頂向下語法分析的充要條件是

產生式A->B|C滿足

  • 如果B和C都不能推導出空,則FIrst(B)交First(C)為空集
  • B和C最多只有一個可以推導出空
  • 如果B可以推導出空,則First(C)交Follow(A為空

LL(1)文法不能由二義性,也不能含有左遞歸,對LL(1)文法的所有句子都可以進行自頂向下的語法分析

並不是所有語言都可以用LL(1)來描述

自下而上的語法分析

FirstVT(T)

非終結符T的最左終結符集合

  • If T->a or T->Ra => a屬於FirstVt(T)
  • if b belongs to FirstVt(R) => b and T->R... 屬於FirstVt(T)
LastVt(T)

非終結符T的最右終結符集合

  • if T->....a or T->....aR => a屬於LastVt(T)
  • if b belongs to LastVt(R) and T->...R => b屬於LastVt(T)
最左素語

complier-leftest

  • 建立一個棧
  • 從左到右掃描表達式
  • 通過出棧比較相鄰兩個個操作符的優先級
  • 如果如果兩個操作符優先級相等或者前一個后一個優先級高則規約前一個操作符
  • 出棧比較相鄰兩個操作符
  • 如果第一個操作符比第二個操作符低,則再取一個操作符,如果第二個操作符等於或者高於第三個操作符,則規約第二個操作符,否則繼續入棧
產生式->NFA
E->aA|bB
A->cA|d
B->cB|d

complier-g2nfa
NFA->DFA

通過子集構造法

complier-g2nfa2dfa

LR(0)語法分析
  • 移進項目:圓點之后為終結符的項目,A->a.bc
  • 待約項目:圓點之后為非終結符,A->a.Bb
  • 歸約項目:圓點之后沒有遠點,A->a.
  • 接受項目:對於文法G(S),有S->Q.

complier-dfa2table
通過從左向右掃描句子,通過下一輸入,決定從Si歸約到Sj,並最終到達終態

SLR(1)語法分析

LR(0)文法,如果遇到在一個狀態中,同時出現移進和規約的沖突,則會讓LR(0)無法判斷此步驟應該如何處理

complier-ll0dame
S3狀態,已經滿足歸約條件,則無論下一個字符是什么,都需要進行歸約操作;但是如果下一個字符是‘,’,則應該i進行移進操作

SLR(1)為了解決移進歸約沖突,則遇到沖突時向前看一個字符,來解決沖突

complier-slr1

LR(1)語法分析

complier-slrdame
當某個狀態遇到移進歸約沖突,並且下一個移進的符號,與歸約符號的下一個符號相同,則無法使用SLR(1)進行分析

SLR(1)通過Follow(A)來計算,而LR(1)通過First(A.next)來決定來縮小范圍

complier-lr1
LR(1)過程:

  • 如果A->a.BM, B->r,m=First(M),則記為B->r,m

中間代碼

優點

  • 編譯程序的邏輯結構更加簡單明確
  • 利於再不同的目標機器上實現同一種語言
  • 利於進行與機器無關的優化
  • 可以用於解析

形式

  • 后綴式
  • 圖表示:抽象語法樹/DAG圖
  • 三地址代碼:三元式/四元式/間接三元式
后綴式
a+b*(c-d)+e/(c-d)
=>
abcd-*+ecd-/+
抽象語法樹

內部節點代表操作符,葉子節點為操作數

complier-abtree

DAG圖

把抽象語法樹的公共部分進行合並,可以減少公共表達式的計算

complier-dag

三地址碼

可以看作抽象語法樹或者DAG的一種線性表示

complier-dag2three

三地址碼詳解

四元式
特點
  • 占用空間多
  • 易於優化
result = arg1 op arg2
=>
(op, arg1 arg2, result)
三元式
特點
  • 占用空間少
  • 由於臨時變量跟i緊密關聯,導致難以優化
result = arg1 op arg2
=>
(i) (op, arg1, arg2)

用i位置來保存臨時結果

左值與右值
x[i]=y =>
(0) ([]=, x, i)
(1) (assign, (0), y)
y=x[i] =>
(0) (=[], x, i)
(1) (assign, y, (0))
間接三元式
特點
  • 占用空間多
  • 易於優化

complier-fourExp

數組翻譯

LOC(A[i]) = base + (i-low)*w
          = (base-low*w) + i*w
		  = conspart + varpart
Loc(A[i][j]) = base + ((i-low1)*n2+j-low)*w
             = (base-(low1*n2+low2)*w)+(i*n2+j)*w

可見任意維度的數組元素的計算都可以分為不變計算和可變計算組成,在編譯時首先計算出conspart,可以達到優化的目的

條件語句的翻譯

文法
S -> if(E) S1|if(E) S1 else S2

complier-if

拉鏈回填

對於E.true的未來地址,在三地址碼中,先通過打標志的方式,在后續確定下地址后,再回填到標志的位置

循環語句的翻譯

文法
S -> while(E) S1

complier-while

符號表

complier-table

存儲

變長字段通過指針鏈接到另外的內存區存儲

查找

線性查找

通過順序查找

自適應線性表:通過新增一列,通過LRU算法,構成鏈表,最新訪問的元素會出現在鏈頭

二叉樹

建立樹索引

hash

建立hash索引

名字作用域

complier-name

嵌套結構的符號表特征
  • 由於函數的執行是先執行,后結束,所以適合用棧的方式來存儲
  • 在函數對應的符號表中指定display表,代表此函數可用符號表的首地址
  • 在符號表中引入指針previous,來連接上一個符號的首地址

運行時存儲空間組織

complier-store

活動記錄

用於管理函數變量的信息

complier-activestore

棧式存儲

complier-activestroe-exapmle

過程進入和返回

通過變更top和sp指針,實現活動記錄的棧式處理

靜態鏈實現局部變量的訪問

complier-staticlink
complier-displaytable

靜態鏈

指向直接外層函數的首地址

動態鏈

指向上一層函數的首地址

display表

所有外層函數的首地址

優化

局部優化

基於基本塊范圍內的優化

  • 刪除公共子表達式
  • 刪除無用代碼
  • 合並已知變量
  • 交換語句位置
  • 代數變換/強度削弱

DAG優化

  1. 三地址碼轉換為DAG

complier-three2dag
2. DAG重寫三地址碼

complier-dagrewritethree
3. 刪除無用變量

complier-removeunused

循環優化

代碼外提
  • 求出循環L的所有不變運算
  • 檢查步驟1的不變運算A=const是否滿足:
    • s是否是L的所有出口必經節點,或者A在離開L后不再活躍
    • A在L的其他地方沒有再賦值
    • 對S涉及的A的引用都是在此A賦值后才到達
  • 對滿足以上條件的A=const進行外提
強度消弱
  • 對於循環L有I = I+C,且有T=K*I+C2,其中C,K,C2為不變量,則T可以進行強度削弱,把乘法轉換為加法
  • 削弱后,循環中會出現無用賦值,可刪除
  • 下標變量的地址計算很耗時,可以使用強度削弱
刪除歸納變量

對於循環變量i,由於L中有其他變量A是跟I有線性關系的,可以用A來代替i在循環的控制,以減少對循環變量的計算


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM