編譯原理 編譯器的編譯基本過程

2021-06-19 17:00:24 字數 2521 閱讀 3730

原文出處: 

崤嶙的部落格

編譯器最基本的功能就是把高階語言(例如c/fortran)編寫的**轉化為機器指令(就是01串),從這個角度來說它本質上是個轉換過程。經典的編譯過程主要包括:

1、詞法分析(lexical analysis)

2、語法分析

語法分析的輸入是一連串的token(詞法分析的輸出),根據語言的語法規則不斷解析最後得到一棵抽象語法樹(ast, abstract syntax tree),負責語法分析模組通常也被叫做parser。在詞法分析中,我們經常使用正規表示式來表示語言所接受的token的規則,類似的,在語法分析中,我們使用文法(grammar)來表示語言的語法規則,這也早期計算機語言設計中的研究熱點(同樣也是大學裡學習編譯時最容易讓人頭暈的東西)。

編譯裡常說的文法指的是一種上下文無關文法(context-free grammar),簡單地說文法裡包含終結符(terminal,就是26個字元、數字等等)、非終結符(nonterminal,實際是一種抽象)和產生式(production)。上下文無關文法要求每個產生式的左邊必須恰好是乙個非終結符,而右邊是0個或多個終結符與非終結符的組合,最後整個文法還必須有乙個起始符(某個終結符)。文法裡還有些很重要的基本概念,例如推導(derivation)、歸約(reduction)、二義性(ambiguity)、最左推導等等。

文法中最重要的基本概念是first集和follow集的構造。根據這兩個集合就可以很容易構造出乙個**分析表,每個行的名字是乙個非終結符,每個列的名字是乙個終結符,如果每個**內沒有兩個以上的項,那麼說明是乙個ll(1)文法(left-to-right parse, leftmost-derivation, 1-symbol lookhead),簡單地說就是向右邊看乙個符號就能確定下一步動作。當原文法不是ll(1)文法時,可以嘗試通過消除左遞迴(eliminate left recursion)和提取左因子(left factoring)對原文法進行變形得到等價的ll(1)文法。

第二種文法就是lr(k)文法(left-to-right parse, rightmost derivation, k-token lookhead)。這種文法的解釋過程一般通過棧輔助實現,中間主要有兩種動作:shift(就是將當前輸入入棧)和reduce(選擇產生式並從棧中彈出符號執行歸約操作)。lr(0)的構成過程就是從起始符所在的產生式開始構造item,然後對每個item針對每個可能的input構造它的出邊(同樣還是乙個item),最終所有的item形成乙個有限狀態機。接下來構造有限狀態機,對於每個狀態,如果出邊是乙個終結符,在對應**記入shift操作,如果是非終結符則記入goto操作。如果s->x.這種item集,那麼對每個終結符r,都記入(s, r)位置處為shift操作。

第三種slr文法與lr(0)非常相似,區別在於生成分析**時,對於s->x.這種item集,僅僅對於r輸入follow(s)才在(s, r)位置處記入shift操作。

最後一種lalr(1)相對於lr(0)而言引入了活字首,構造思路仍與lr(0)類似,但是構造出來的**分析表更大。

綜合起來,各文法表述能力:ll(0)3、語義分析(sematic analysis)

語義分析包括一些經典的問題。

(1)型別檢查(type checking),例如在語法樹上a+b看起來是沒問題的,因為a和b都是合法的變數名,並且語法中支援變數間+這種操作。但是可能a是乙個字串,而b是乙個浮點數,這兩者之間的+操作就不符合語義規範了,這種問題在這個階段都會被找出來。

(2)符號管理,最經典的問題就是如何管理變數(變數的名字,型別,變數的作用域(scope)等),在分析**時,符號管理肯定是被頻繁的搜尋,因此它通常會使用hash來組織。

4、中間**(ir, intermediate representation)生成

ir是非常非常重要的,它被引入的初衷是提高編譯器開發的效率。ir是編譯過程的乙個匯聚點,在ir之前我們通常都認為是編譯的前端,而ir之後是編譯的後端,這樣當編譯器需要多支援一種高階語言時主要工作就是提供乙個前端,而當需要移植到一種新的平台上時主要工作就是提供對應的後端。關於ir的表示典型的有三位址碼。ir生成的過程就是將一棵抽象語法樹(這是編譯器前端對源**的理解和抽象)變成一串ir定義的**(ir指令種類簡單,這便於優化)。這個轉換過程都是比較固定的套路,例如if-else,while/for等基本結構如何轉都是乙個固定的套路。

5、編譯優化

這一部分是現代編譯器最核心所在,主要有兩類,一類是通用的優化手段,比如死**刪除、迴圈不變數外提、強度削弱等,另一類就是體系結構相關的,說白了就是某種體系結構針對某類應用提供了特殊指令,例如intel的mmx,sse2等等。為支援優化工作的開展,我們首先需要能夠比較方便的描述**。最基本的當然是一條指令,但是這個太細微,於是往上抽象出基本塊(basic block),這個基本上是所有優化開展必備的工作,然後多個基本塊還可以構成乙個超級塊(region)。此外,經典的方法還包括控制流分析和資料流分析,這裡常用的包括d-u鏈等。最後乙個經典的topic就是暫存器分配。

6、目標**生成

這裡直接和具體平台相關,這裡的平台同時包括軟體和硬體,例如哪種目標檔案格式(elf, pe),哪種平台(指令集)。不過現在編譯器一般生成的是字元形式的彙編檔案,所以前面乙個問題基本不大,主要影響在後者。

編譯器的編譯基本過程

編譯器最基本的功能就是把高階語言 例如c fortran 編寫的 轉化為機器指令 就是01串 從這個角度來說它本質上是個轉換過程。經典的編譯過程主要包括 1 詞法分析 lexical analysis 語法分析的輸入是一連串的token 詞法分析的輸出 根據語言的語法規則不斷解析最後得到一棵抽象語法...

c 編譯器編譯過程

編譯過程 編譯過程分為四步 預處理 編譯 彙編 連線 1.預處理 預處理主要處理原始檔中的 include define 等預處理等命令 預處理主要完成的工作有 參考 程式設計師的自我修養 1 刪除 define,展開巨集 2 處理條件編譯指令。預處理程式先判斷條件,再根據條件修改源 3 刪除注釋 ...

方舟編譯器編譯過程

開發環境推薦ubuntu16.04 sudo apt get y install openjdk 8 jdk git core gnupg flex bison gperf build essential zip curl zlib1g dev libc6 dev i386 lib32ncurses...