亚洲最大看欧美片,亚洲图揄拍自拍另类图片,欧美精品v国产精品v呦,日本在线精品视频免费

  • 站長資訊網(wǎng)
    最全最豐富的資訊網(wǎng)站

    go語言是否需要編譯

    go語言需要編譯。Go語言是編譯型的靜態(tài)語言,是一門需要編譯才能運(yùn)行的編程語言,也就說Go語言程序在運(yùn)行之前需要通過編譯器生成二進(jìn)制機(jī)器碼(二進(jìn)制的可執(zhí)行文件),隨后二進(jìn)制文件才能在目標(biāo)機(jī)器上運(yùn)行。

    go語言是否需要編譯

    php入門到就業(yè)線上直播課:進(jìn)入學(xué)習(xí)
    Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點(diǎn)擊使用

    本教程操作環(huán)境:windows7系統(tǒng)、GO 1.18版本、Dell G3電腦。

    Go語言是一門需要編譯才能運(yùn)行的編程語言,也就說代碼在運(yùn)行之前需要通過編譯器生成二進(jìn)制機(jī)器碼,隨后二進(jìn)制文件才能在目標(biāo)機(jī)器上運(yùn)行。

    簡單來說,Go語言是編譯型的靜態(tài)語言(和C語言一樣),所以在運(yùn)行Go語言程序之前,先要將其編譯成二進(jìn)制的可執(zhí)行文件。

    如果我們想要了解Go語言的實(shí)現(xiàn)原理,理解它的編譯過程就是一個(gè)沒有辦法繞過的事情。下面就來看看Go語言是怎么完成編譯的。

    預(yù)備知識

    想要深入了解Go語言的編譯過程,需要提前了解一下編譯過程中涉及的一些術(shù)語和專業(yè)知識。這些知識其實(shí)在我們的日常工作和學(xué)習(xí)中比較難用到,但是對于理解編譯的過程和原理還是非常重要的。

    1) 抽象語法樹

    在計(jì)算機(jī)科學(xué)中,抽象語法樹(Abstract Syntax Tree,AST),或簡稱語法樹(Syntax tree),是源代碼語法結(jié)構(gòu)的一種抽象表示。它以樹狀的形式表現(xiàn)編程語言的語法結(jié)構(gòu),樹上的每個(gè)節(jié)點(diǎn)都表示源代碼中的一種結(jié)構(gòu)。

    之所以說語法是“抽象”的,是因?yàn)檫@里的語法并不會表示出真實(shí)語法中出現(xiàn)的每個(gè)細(xì)節(jié)。比如,嵌套括號被隱含在樹的結(jié)構(gòu)中,并沒有以節(jié)點(diǎn)的形式呈現(xiàn)。而類似于 if else 這樣的條件判斷語句,可以使用帶有兩個(gè)分支的節(jié)點(diǎn)來表示。

    以算術(shù)表達(dá)式 1+3*(4-1)+2 為例,可以解析出的抽象語法樹如下圖所示:

    go語言是否需要編譯

    圖:抽象語法樹

    抽象語法樹可以應(yīng)用在很多領(lǐng)域,比如瀏覽器,智能編輯器,編譯器。

    2) 靜態(tài)單賦值

    在編譯器設(shè)計(jì)中,靜態(tài)單賦值形式(static single assignment form,通常簡寫為 SSA form 或是 SSA)是中介碼(IR,intermediate representation)的屬性,它要求每個(gè)變量只分配一次,并且變量需要在使用之前定義。在實(shí)踐中我們通常會用添加下標(biāo)的方式實(shí)現(xiàn)每個(gè)變量只能被賦值一次的特性,這里以下面的代碼舉一個(gè)簡單的例子:

    x := 1 x := 2 y := x
    登錄后復(fù)制

    從上面的描述所知,第一行賦值行為是不需要的,因?yàn)?x 在第二行被二度賦值并在第三行被使用,在 SSA 下,將會變成下列的形式:

    x1 := 1 x2 := 2 y1 := x2
    登錄后復(fù)制

    從使用 SSA 的中間代碼我們就可以非常清晰地看出變量 y1 的值和 x1 是完全沒有任何關(guān)系的,所以在機(jī)器碼生成時(shí)其實(shí)就可以省略第一步,這樣就能減少需要執(zhí)行的指令來優(yōu)化這一段代碼。

    根據(jù) Wikipedia(維基百科)對 SSA 的介紹來看,在中間代碼中使用 SSA 的特性能夠?yàn)檎麄€(gè)程序?qū)崿F(xiàn)以下的優(yōu)化:

    • 常數(shù)傳播(constant propagation)
    • 值域傳播(value range propagation)
    • 稀疏有條件的常數(shù)傳播(sparse conditional constant propagation)
    • 消除無用的程式碼(dead code elimination)
    • 全域數(shù)值編號(global value numbering)
    • 消除部分的冗余(partial redundancy elimination)
    • 強(qiáng)度折減(strength reduction)
    • 寄存器分配(register allocation)

    因?yàn)?SSA 的作用的主要作用就是代碼的優(yōu)化,所以是編譯器后端(主要負(fù)責(zé)目標(biāo)代碼的優(yōu)化和生成)的一部分。當(dāng)然,除了 SSA 之外代碼編譯領(lǐng)域還有非常多的中間代碼優(yōu)化方法,優(yōu)化編譯器生成的代碼是一個(gè)非常古老并且復(fù)雜的領(lǐng)域,這里就不展開介紹了。

    3) 指令集架構(gòu)

    最后要介紹的一個(gè)預(yù)備知識就是指令集架構(gòu)了,指令集架構(gòu)(Instruction Set Architecture,簡稱 ISA),又稱指令集或指令集體系,是計(jì)算機(jī)體系結(jié)構(gòu)中與程序設(shè)計(jì)有關(guān)的部分,包含了基本數(shù)據(jù)類型,指令集,寄存器,尋址模式,存儲體系,中斷,異常處理以及外部 I/O。指令集架構(gòu)包含一系列的 opcode 即操作碼(機(jī)器語言),以及由特定處理器執(zhí)行的基本命令。

    指令集架構(gòu)常見種類如下:

    • 復(fù)雜指令集運(yùn)算(Complex Instruction Set Computing,簡稱 CISC);
    • 精簡指令集運(yùn)算(Reduced Instruction Set Computing,簡稱 RISC);
    • 顯式并行指令集運(yùn)算(Explicitly Parallel Instruction Computing,簡稱 EPIC);
    • 超長指令字指令集運(yùn)算(VLIW)。

    不同的處理器(CPU)使用了大不相同的機(jī)器語言,所以我們的程序想要在不同的機(jī)器上運(yùn)行,就需要將源代碼根據(jù)架構(gòu)編譯成不同的機(jī)器語言。

    編譯原理

    Go語言編譯器的源代碼在 cmd/compile 目錄中,目錄下的文件共同構(gòu)成了Go語言的編譯器,學(xué)過編譯原理的人可能聽說過編譯器的前端和后端,編譯器的前端一般承擔(dān)著詞法分析、語法分析、類型檢查和中間代碼生成幾部分工作,而編譯器后端主要負(fù)責(zé)目標(biāo)代碼的生成和優(yōu)化,也就是將中間代碼翻譯成目標(biāo)機(jī)器能夠運(yùn)行的機(jī)器碼。

    go語言是否需要編譯

    Go的編譯器在邏輯上可以被分成四個(gè)階段:詞法與語法分析、類型檢查和 AST 轉(zhuǎn)換、通用 SSA 生成和最后的機(jī)器代碼生成,下面我們來分別介紹一下這四個(gè)階段做的工作。

    1) 詞法與語法分析

    所有的編譯過程其實(shí)都是從解析代碼的源文件開始的,詞法分析的作用就是解析源代碼文件,它將文件中的字符串序列轉(zhuǎn)換成 Token 序列,方便后面的處理和解析,我們一般會把執(zhí)行詞法分析的程序稱為詞法解析器(lexer)。

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

    SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" }
    登錄后復(fù)制

    標(biāo)準(zhǔn)的 Golang 語法解析器使用的就是 LALR(1) 的文法,語法解析的結(jié)果其實(shí)就是上面介紹過的抽象語法樹(AST),每一個(gè) AST 都對應(yīng)著一個(gè)單獨(dú)的Go語言文件,這個(gè)抽象語法樹中包括當(dāng)前文件屬于的包名、定義的常量、結(jié)構(gòu)體和函數(shù)等。

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

    2) 類型檢查

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

    • 常量、類型和函數(shù)名及類型;
    • 變量的賦值和初始化;
    • 函數(shù)和閉包的主體;
    • 哈希鍵值對的類型;
    • 導(dǎo)入函數(shù)體;
    • 外部的聲明;

    通過對每一棵抽象節(jié)點(diǎn)樹的遍歷,我們在每一個(gè)節(jié)點(diǎn)上都會對當(dāng)前子樹的類型進(jìn)行驗(yàn)證保證當(dāng)前節(jié)點(diǎn)上不會出現(xiàn)類型錯(cuò)誤的問題,所有的類型錯(cuò)誤和不匹配都會在這一個(gè)階段被發(fā)現(xiàn)和暴露出來。

    類型檢查的階段不止會對樹狀結(jié)構(gòu)的節(jié)點(diǎn)進(jìn)行驗(yàn)證,同時(shí)也會對一些內(nèi)建的函數(shù)進(jìn)行展開和改寫,例如 make 關(guān)鍵字在這個(gè)階段會根據(jù)子樹的結(jié)構(gòu)被替換成 makeslice 或者 makechan 等函數(shù)。

    其實(shí)類型檢查不止對類型進(jìn)行了驗(yàn)證工作,還對 AST 進(jìn)行了改寫以及處理Go語言內(nèi)置的關(guān)鍵字,所以,這一過程在整個(gè)編譯流程中是非常重要的,沒有這個(gè)步驟很多關(guān)鍵字其實(shí)就沒有辦法工作?!?/p>

    贊(0)
    分享到: 更多 (0)
    網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號