Go compile

AST全稱Abstract Syntax Tree抽象語法樹宛渐,即以樹狀形式表現(xiàn)變成語言的語法結(jié)構(gòu),樹上每個節(jié)點都表示源代碼中一種結(jié)構(gòu)。之所以說語法是抽象的垒棋,是因為這里的語法并不會表示出真實語法中出現(xiàn)的每個細(xì)節(jié)采呐。

Golang語法樹是Go源代碼的另一種語義等價的表現(xiàn)形式外厂,Go自帶的go fmt蛤售、go doc等命令都是在Go語法樹基礎(chǔ)之上的分析工具,將Go語言程序作為輸入數(shù)據(jù)瓜贾,從語法樹的維度重新審視Go語言程序。

$ go run

Golang的go run命令會完成源代碼從編譯到執(zhí)行的過程吴叶,簡單來說可將go run等價于go build + 執(zhí)行阐虚。

$ go build
參數(shù) 描述
go build -n 不執(zhí)行地打印流程中用的命令
go build -x 執(zhí)行并打印流程中使用到的命令
go build -work 打印編譯時的臨時目錄路徑,結(jié)束時保留蚌卤,默認(rèn)編譯結(jié)束會刪除实束。

Golang中go build主要完成源碼的編譯和可執(zhí)行文件的生成。go build接收參數(shù)為.go文件或目錄逊彭,默認(rèn)情況下編譯當(dāng)前目錄下所有的.go文件咸灿。在main包下執(zhí)行會生成相應(yīng)的可執(zhí)行文件,在非main包下會做一些檢查侮叮,生成的庫文件放在緩存目錄下避矢,在工作目錄下并無新文件生成。

編譯過程

Golang是需要編譯才能執(zhí)行的語言囊榜,代碼在運行前需要通過編譯器生成二進(jìn)制機(jī)器碼审胸,隨后二進(jìn)制機(jī)器碼才能在目標(biāo)機(jī)器上運行。

Golang編碼程序首先經(jīng)過編譯器生成plan9匯編卸勺,再由匯編器和鏈接處理得到最終的可執(zhí)行程序砂沛。

編譯過程

Golang編譯器的源代碼位于cmd/compile目錄下,目錄下的文件共同構(gòu)成了Golang的編譯器曙求,編譯器分為前端和后端碍庵,前端承擔(dān)著詞法分析映企、語法分析、類型檢查静浴、中間代碼生成的工作堰氓,后端主要負(fù)責(zé)目標(biāo)代碼的生成和優(yōu)化,就是將中間代碼翻譯成機(jī)器能夠運行的機(jī)器碼苹享。

編譯過程

源代碼從編譯到執(zhí)行的過程會經(jīng)過:源碼->編譯->可執(zhí)行文件->執(zhí)行輸出

Golang的編譯器對Golang源代碼的處理在邏輯上可分為四個階段

1.詞法語法分析
2.類型檢查和AST轉(zhuǎn)換
3.SSA優(yōu)化和降級轉(zhuǎn)換
4.Go源碼生成對應(yīng)的plan9匯編

編譯過程

Golang程序的編譯入口是compile/internal/gc/main.go文件的Main函數(shù)双絮,Main函數(shù)獲取命令行參數(shù)并更新編譯選項和配置,然后運行parseFiles函數(shù)對輸入的所有文件進(jìn)行詞法和語法分析富稻,得到對應(yīng)的AST抽象語法樹掷邦。

第一階段:詞法和語法分析

編譯過程是從解析代碼的源文件開始,詞法分析的作用是解析源代碼文件椭赋,將文件中的字符串序列轉(zhuǎn)換為Token序列抚岗,以方便后續(xù)的處理和解析,一般會把執(zhí)行詞法分析的程序稱為詞法解析器(lexer)哪怔。

順序 階段 工具 描述
1 詞法分析 詞法分析器 源代碼被token
2 語法分析 解析器 解析
3 生成語法樹 抽象語法樹 為每個源構(gòu)造語法樹文件

1.詞法分析

  • 詞法分析是將字符串轉(zhuǎn)換為標(biāo)記(Token)序列的過程

詞法分析的輸入是詞法分析器輸出的Token序列宣蔚,Token序列會按順序被語法分析器進(jìn)行解析,語法的解析過程是將詞法分析生成的Token按照語言定義好的文法(Grammar)自下而上或自上而下的進(jìn)行規(guī)約认境,每個Go的源代碼文件最終會被歸納成一個SourceFile結(jié)構(gòu)胚委。

1SourceFile = PackageClause ";" {ImportDecl";"} {TopLevelDecl";"}

源代碼被詞法分析器Token化后進(jìn)行詞法分析,解析器解析后進(jìn)行語法分析叉信,最后為每個源構(gòu)造語法樹文件亩冬。每個語法樹都由與之對應(yīng)的源文件上元素的節(jié)點,比如表達(dá)式硼身、聲明硅急、陳述。

"json.go":SourceFile {
  PackageName:"json",
  ImportDecl: []Import{"io"},
  TopLevelDecl:...
}

Golang中compile/internal/syntax/tokens.go文件定義了Golang支持的全部token類型佳遂,比如名稱和文本营袜、操作符、定界符丑罪、關(guān)鍵字等荚板。詞法分析會將文本中的字符序列轉(zhuǎn)換為標(biāo)記序列,比如將關(guān)鍵字package轉(zhuǎn)換為_package標(biāo)記吩屹,fun轉(zhuǎn)換為_fun標(biāo)記等跪另。

Golang的詞法解析是通過src/cmd/compile/internal/syntax/scanner.go文件中的syntax.scanner結(jié)構(gòu)體實現(xiàn)的,由syntax.scanner.next方法驅(qū)動煤搜。

src/cmd/compile/internal/syntax/scanner.go文件實現(xiàn)了詞法解析器免绿,使用scanner結(jié)構(gòu)的next方法實現(xiàn).go文件的掃描并轉(zhuǎn)換為token序列。next方法獲取文件中未被解析的字符宅楞,進(jìn)入switch/case分支進(jìn)行詞法解析针姿。

2.語法分析

  • 語法分析是根據(jù)某種特定的形式文法(Grammer)對Token序列構(gòu)成的輸入文本進(jìn)行分析并確定其語法結(jié)構(gòu)的過程。

標(biāo)準(zhǔn)的Golang語法分析器使用的是LALR的文法厌衙,語法解析的結(jié)果是抽象語法樹距淫,每個AST都對應(yīng)著一個單獨的Golang文件,這個抽象語法樹包括當(dāng)前文件屬于的包名婶希、定義的常量榕暇、結(jié)構(gòu)體、函數(shù)等喻杈。

Golang-files-and-ast

語法分析會通過文法分析彤枢,構(gòu)建輸入的token序列的語法結(jié)構(gòu),得到對應(yīng)的語法樹筒饰。

語法分析

如果在語法解析過程中發(fā)生了任何語法錯誤缴啡,都會被語法解析器發(fā)現(xiàn)并將消息打印到標(biāo)準(zhǔn)輸出上,整個編譯過程也會隨著錯誤的出現(xiàn)而被中止瓷们。

語法解析過程會調(diào)用goroutine业栅,語法解析器位于cmd/compile/internal/syntax.parser,其中syntax.parser.fileOrNil為一個核心解析方法谬晕。解析的產(chǎn)物是文件對應(yīng)的抽象語法樹碘裕。

3.抽象語法樹

抽象語法樹(AST)是源代碼語法結(jié)構(gòu)一種抽象的表示,抽象語法樹使用樹狀的方式表示編程語言中的語法結(jié)構(gòu)攒钳。抽象語法樹中每個節(jié)點表示源代碼的一個元素帮孔,每個子樹表示一個語法元素。

作為編譯器中常用的數(shù)據(jù)結(jié)構(gòu)不撑,抽象語法樹會抹去源代碼中不重要的字符文兢,比如空格、分號燎孟、括號等禽作。編譯器在執(zhí)行完語法分析后會輸出一個抽象語法樹,抽象語法樹會輔助編譯器進(jìn)行語義分析揩页,以此來確定結(jié)構(gòu)正確的程序是否存在類型不匹配或不一致的問題旷偿。

第二階段:語義分析

語義分析主要包括對抽象語法樹(AST)進(jìn)行類型檢查和變換

語義分析過程中重要的操作:逃逸分析、變量捕獲爆侣、函數(shù)內(nèi)聯(lián)萍程、閉包處理

1. 類型檢查

  • 通過名稱解析和類型推斷以確定對象所屬的標(biāo)識符,以及每個表達(dá)式具有的類型兔仰。
  • 類型檢查包括某些額外的檢查茫负,比如“聲明和未使用”以及確定函數(shù)是否終止。

類型檢查

當(dāng)拿到一組文件的抽象語法樹之后乎赴,Golang的編譯器會對語法樹中定義和使用的類型進(jìn)行檢查忍法,類型檢查分別會按照順序?qū)Σ煌愋偷墓?jié)點進(jìn)行驗證潮尝,會按照如下順序進(jìn)行處理:

  1. 常量、類型饿序、函數(shù)名及類型
  2. 變量的賦值和初始化
  3. 函數(shù)和閉包的主體
  4. 哈希鍵值對的類型
  5. 導(dǎo)入函數(shù)體
  6. 外部的聲明

通過對每顆抽象結(jié)點樹的遍歷勉失,會在每個節(jié)點上都會對當(dāng)前子樹的類型進(jìn)行驗證保證當(dāng)前節(jié)點上不會出現(xiàn)類型錯誤的問題,所有的類型錯誤和不匹配都會在此階段被發(fā)現(xiàn)和暴露出來原探。

類型檢查不止會對樹狀結(jié)構(gòu)的節(jié)點進(jìn)行驗證乱凿,同時也會對一些內(nèi)建的函數(shù)進(jìn)行展開和改寫。比如make關(guān)鍵字會在此階段會根據(jù)子樹的結(jié)構(gòu)被替換成為makeslicemakechan等函數(shù)咽弦。

golang-keyword-make

2. 變換

  • 在抽象語法樹上進(jìn)行某些轉(zhuǎn)換徒蟆,某些節(jié)點基于類型信息會被細(xì)化。比如:從算術(shù)加法節(jié)點類型分割的字符串添加型型、死代碼消除段审、函數(shù)調(diào)用內(nèi)聯(lián)、轉(zhuǎn)義分析...

第三階段:SSA生成(中間代碼生成)

中間代碼是指一種應(yīng)用于抽象機(jī)器的編程語言输莺,其設(shè)計目的是用來幫助我們分析計算機(jī)程序戚哎。在編譯過程中,編譯器會將源代碼轉(zhuǎn)換成目標(biāo)機(jī)器上機(jī)器的過程中嫂用,先把原地阿瑪轉(zhuǎn)換成一種中間的表述形式型凳。

當(dāng)將源文件轉(zhuǎn)換成抽象語法樹、對整顆樹的語法進(jìn)行解析并進(jìn)行類型檢查之后嘱函,可以認(rèn)為當(dāng)前文件中的代碼基本上不存在無法編譯或語法錯誤的問題甘畅,Golang的編譯器會將輸入的AST轉(zhuǎn)換成為中間代碼。

  • Golang編譯器的中間代碼具有靜態(tài)單賦值(SSA)的特性

Golang編譯器的中間代碼使用了SSA(Static Single Assignment Form往弓,靜態(tài)單一分配)的特性疏唾,如果在中間代碼生成的過程中使用此特性,就能夠很容易的分析出代碼中無用的變量和片段并對代碼進(jìn)行優(yōu)化函似。

類型檢查之后會通過名為compileFunctions的函數(shù)開始對整個Golang中的全部函數(shù)進(jìn)行編譯槐脏,這些函數(shù)會在一個編譯隊列中等待幾個后端工作goroutine的消費,這些goroutine會將所有函數(shù)對應(yīng)的AST轉(zhuǎn)換為使用SSA特性的中間代碼撇寞。

抽象語法樹(AST)將轉(zhuǎn)換為靜態(tài)單一分配(SSA)形式顿天,SSA是一種具有特定屬性的低級中間表示,可以更加輕松地實現(xiàn)優(yōu)化并最終從中生成機(jī)器代碼蔑担。

在此轉(zhuǎn)換期間會將應(yīng)用函數(shù)內(nèi)在函數(shù)牌废,對于這些特殊功能,編譯器會教導(dǎo)它們根據(jù)具體情況使用大量優(yōu)化的代碼替代啤握。

在AST到SSA轉(zhuǎn)換期間鸟缕,某些節(jié)點會被降級為更為簡單的組件,因此編譯器的其余部分可使用它們。例如內(nèi)置復(fù)制替代為內(nèi)存移動懂从,并且范圍循環(huán)被重寫為for循環(huán)授段。

然后,應(yīng)用一系列與機(jī)器無關(guān)的傳遞和規(guī)則番甩。這些不會涉及任何單個計算機(jī)體系結(jié)構(gòu)畴蒲,因此看在所有GOARCH變體上運行。

這些通用過程的示例包括消除死代碼对室,刪除不需要的零檢查、刪除未使用的分支咖祭。

通過重寫規(guī)則主要涉及表達(dá)式掩宜,比如使用常量值替換某些表達(dá)式,以及優(yōu)化乘法和浮點運算么翰。

第四階段:機(jī)器碼生成

  1. 底層SSA和結(jié)構(gòu)特定的傳遞
  2. 生成機(jī)器碼

Golang源代碼的cmd/compile/internal中包含了許多機(jī)器代生成相關(guān)的包牺汤,不同類型的CPU分別使用不同的包進(jìn)行生成amd64arm浩嫌、arm64檐迟、mipsmips64码耐、ppc64追迟、s390xx86骚腥、wasm敦间。Golang能夠在上述的CPU指令集類型上運行。

作為一種在棧虛擬機(jī)上使用的二進(jìn)制指令格式束铭,它的設(shè)計目標(biāo)是在Web瀏覽器上提供一種具有高可移植性的目標(biāo)語言廓块。Golang編譯器既能生成WASM格式的指令,就能運行在常見的主流瀏覽器中契沫。

1$ GOARCH=wasm GOOS=js gobuild -o lib.wasm main.go

編譯器的機(jī)器相關(guān)階段以底層傳遞開始带猴,該傳遞將通用值重寫為其機(jī)器特定的變體。例如在AMD64存儲器操作數(shù)上是可能的懈万,因此可以組合許多加載存儲操作拴清。

需要注意的是,較低的通道運行所有特定于機(jī)器的重寫規(guī)則钞速,因此它當(dāng)前也應(yīng)用了大量優(yōu)化贷掖。

一旦SSA降低且更加特定于目標(biāo)體系結(jié)構(gòu),就會運行最終的代碼優(yōu)化過程渴语。這包括另一個四代碼消除傳遞苹威,移動值更接近它們的使用,刪除從未讀取的局部變量驾凶,以及寄存器分配牙甫。

在SSA生成節(jié)點結(jié)束時掷酗,Go函數(shù)已轉(zhuǎn)換為一些列obj.Prog指令。它們會被傳遞給裝載器cmd/internal/obj窟哺,將它們轉(zhuǎn)換為機(jī)器代碼并寫出最終的目標(biāo)文件泻轰。目標(biāo)文件還將包含反射數(shù)據(jù),導(dǎo)出數(shù)據(jù)和調(diào)試信息且轨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浮声,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子旋奢,更是在濱河造成了極大的恐慌泳挥,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件至朗,死亡現(xiàn)場離奇詭異屉符,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锹引,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門矗钟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嫌变,你說我怎么就攤上這事吨艇。” “怎么了腾啥?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵秸应,是天一觀的道長。 經(jīng)常有香客問我碑宴,道長软啼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任延柠,我火速辦了婚禮祸挪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贞间。我一直安慰自己贿条,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布增热。 她就那樣靜靜地躺著整以,像睡著了一般。 火紅的嫁衣襯著肌膚如雪峻仇。 梳的紋絲不亂的頭發(fā)上公黑,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼凡蚜。 笑死人断,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的朝蜘。 我是一名探鬼主播恶迈,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谱醇!你這毒婦竟也來了暇仲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤副渴,失蹤者是張志新(化名)和其女友劉穎熔吗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佳晶,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年讼载,在試婚紗的時候發(fā)現(xiàn)自己被綠了轿秧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡咨堤,死狀恐怖菇篡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情一喘,我是刑警寧澤驱还,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凸克,受9級特大地震影響议蟆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜萎战,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一咐容、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚂维,春花似錦戳粒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涂籽,卻和暖如春苹祟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工苔咪, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留锰悼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓团赏,卻偏偏與公主長得像箕般,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舔清,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容