自編教材分享:第五章—編譯與運(yùn)行優(yōu)化(一)


編譯流程
編譯器的工作流程可分為預(yù)編譯、編譯、匯編以及鏈接四個(gè)階段,其中,預(yù)編譯又稱預(yù)處理,是整個(gè)編譯過程最先做的工作,主要是代碼級(jí)的文本處理工作;編譯過程是把預(yù)編譯生成代碼文件翻譯為目標(biāo)機(jī)器匯編代碼;匯編過程是將匯編代碼翻譯為機(jī)器相關(guān)的二進(jìn)制目標(biāo)文件,并最終由鏈接器對(duì)目標(biāo)文件、操作系統(tǒng)的啟動(dòng)代碼和用到的庫文件進(jìn)行組織,將各個(gè)模塊的代碼鏈接到一起,最終生成可執(zhí)行程序。
目前市面上較為流行的編譯器有GCC、LLVM,以及Intel-ICC編譯器,其中GCC是由GNU開發(fā)的開源編譯器,LLVM是伊利諾伊大學(xué)發(fā)起的一個(gè)開源項(xiàng)目,相較于GCC,LLVM的模塊化和復(fù)用性更好,且速度更快。

LLVM編譯器架構(gòu)
本次分享內(nèi)容以LLVM編譯器為主,LLVM是構(gòu)架編譯器的框架系統(tǒng),以C++編寫而成,支持多種語言和后端。
LLVM的編譯流程分為前中后三個(gè)部分,前端的工作主要針對(duì)源語言,通過分析高級(jí)語言代碼的文本,相應(yīng)的進(jìn)行預(yù)編譯、詞法分析、語法分析、語義分析到生成中間代碼,編譯器中端主要對(duì)前端生成的中間代碼進(jìn)行優(yōu)化,其中包括常規(guī)編譯優(yōu)化、過程間優(yōu)化、循環(huán)優(yōu)化、自動(dòng)向量化及其它優(yōu)化。編譯器后端重點(diǎn)關(guān)注目標(biāo)機(jī)器,對(duì)中間代碼實(shí)施面向目標(biāo)機(jī)器特征的優(yōu)化,生成符合目標(biāo)機(jī)器運(yùn)行需要的匯編代碼,這些代碼最終通過匯編器和鏈接器生成在目標(biāo)機(jī)器上可執(zhí)行的二進(jìn)制程序。
接下來會(huì)展開介紹每個(gè)階段所做的工作。

編譯器前端
預(yù)編譯
前端要做的第一個(gè)工作為預(yù)編譯,完成文件包含的插入、宏展開、條件編譯展開和刪除注釋等任務(wù)。
(1)文件包含:指源代碼文件中的#include文件包含聲明。例如,當(dāng)源代碼文件中含有語句#include <stdio.h>時(shí),預(yù)處理器會(huì)在系統(tǒng)標(biāo)準(zhǔn)路徑下搜索C的標(biāo)準(zhǔn)輸入輸出頭文件stdio.h,將文件stdio.h中的代碼復(fù)制到當(dāng)前文件以來代替上述文件擴(kuò)展聲明語句。
(2)宏展開:C程序中可以使用#define來定義宏,一個(gè)宏定義給出一段C代碼的縮寫。預(yù)處理器將源程序文件中出現(xiàn)的、對(duì)宏的引用展開成相應(yīng)的宏定義。
(3) 條件編譯:處理#if和#ifdef等條件編譯指令,將源代碼中的某部分代碼包含進(jìn)來或排除在外。
(4) 刪除注釋:刪除所有的注釋“//”和“/* */”。
下邊代碼為生成的部分預(yù)編譯文件,可以明顯看出在主程序部分,已經(jīng)沒有注釋,并且數(shù)組d[N]也被替換為了d[1024]。
詞法分析
詞法分析器讀入程序的源代碼字符流,掃描、分解字符串,識(shí)別出一個(gè)個(gè)的單詞,包括如下類型:
關(guān)鍵字,如int、float、if、 sizeof等。
標(biāo)識(shí)符,用來表示各種名字,如變量、數(shù)組名、函數(shù)名等。
運(yùn)算符,包括算術(shù)運(yùn)算符、邏輯運(yùn)算符、關(guān)系運(yùn)算符等。
分解符,包括“,、;”等符號(hào)。
常數(shù),包括整形、浮點(diǎn)型、字符型等。

查看LLVM詞法分析過程的編譯命令:
語法分析
語法分析的任務(wù)是將詞法分析生成的單詞組合成語法短語,同時(shí)分析這些短語是否符合高級(jí)程序設(shè)計(jì)語言中的語法規(guī)則。
有下面的規(guī)則來定義表達(dá)式:
標(biāo)識(shí)符是表達(dá)式。
常數(shù)是表達(dá)式。
若表達(dá)式1和表達(dá)式2都是表達(dá)式,那么表達(dá)式1+表達(dá)式2以及表達(dá)式1 * 表達(dá)式2也都是表達(dá)式。
有下面的規(guī)則來定義賦值語句:
<表達(dá)式> = n
<表達(dá)式> = <表達(dá)式>“+”<表達(dá)式>
<表達(dá)式> = <表達(dá)式>“* ”<表達(dá)式>
<賦值語句> = <標(biāo)識(shí)符>“=”<表達(dá)式>

以語句c = a + b * 3為例,依據(jù)高級(jí)程序設(shè)計(jì)語言中約定的賦值語句及表達(dá)式的定義規(guī)則表示為抽象語法樹形式。將字符串格式的源代碼轉(zhuǎn)化為樹狀的數(shù)據(jù)結(jié)構(gòu),更容易被計(jì)算機(jī)理解和處理。

語義分析
語義分析階段的任務(wù)是審查源代碼有無語義錯(cuò)誤,源代碼中有些語法成分,按照語法規(guī)則去判斷是正確的,但不符合語義規(guī)則,比如使用了沒有聲明的變量。
語義分析主要的任務(wù)可歸結(jié)為以下四類:
完成靜態(tài)語義審查和處理;
上下文相關(guān)性審查;
類型匹配審查;
類型轉(zhuǎn)換。
比如語句c=a+b*3中,運(yùn)算符*的兩個(gè)運(yùn)算對(duì)象分別是b和3,如果b是實(shí)型變量,3是整型常數(shù),語義分析階段執(zhí)行類型審查之后,會(huì)自動(dòng)地將整型量轉(zhuǎn)換為實(shí)型量以完成同類型的數(shù)據(jù)運(yùn)算。效果會(huì)體現(xiàn)在語法分析所得到的語法樹上,即增加一個(gè)運(yùn)算符結(jié)點(diǎn)(inttoreal)。

編譯器前端-相關(guān)選項(xiàng)
編譯器前端將高級(jí)程序設(shè)計(jì)語言編寫的源代碼翻譯到統(tǒng)一中間表示,優(yōu)化人員可以通過編譯選項(xiàng)指定預(yù)處理、語言和模式等對(duì)程序的前端編譯過程予以干預(yù)。

