0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

編譯器把代碼轉(zhuǎn)化為機(jī)器碼的過(guò)程

xCb1_yikoulinux ? 來(lái)源:一口Linux ? 作者:一口Linux ? 2022-08-03 13:56 ? 次閱讀

編譯器,是把高級(jí)語(yǔ)言轉(zhuǎn)化為機(jī)器語(yǔ)言的工具軟件。

高級(jí)語(yǔ)言的代碼也是個(gè)文本字符串,所以編譯器的前端與sed、gawk、grep是差不多的,都是廣義上的字符串匹配

編譯器把代碼轉(zhuǎn)化為機(jī)器碼過(guò)程如下:

1,詞法分析,

這是編譯器里最簡(jiǎn)單的模塊。

詞法分析,就是通過(guò)查看下一個(gè)字符來(lái)確定怎么把代碼字符串分割成一個(gè)個(gè)的語(yǔ)法詞匯。

起始符、終止符,是詞法分析的主要概念。

intday =24 * 3600;

這行代碼的第1個(gè)詞是int,起始符是i,終止符是空格

在詞法分析時(shí)它是一個(gè)標(biāo)志符,也就是字母、下劃線、數(shù)字組成的一個(gè)字符串,必須以字母或下劃線開(kāi)頭。

在分析出int這個(gè)標(biāo)志符之后,它后面的空格就沒(méi)用了,直接丟棄它。

第2個(gè)詞day也是一個(gè)標(biāo)志符,起始符是d,終止符也是空格。

習(xí)慣把代碼寫(xiě)得密集的人可能這么寫(xiě):int day=24*3600;

這時(shí)day的終止符是=,它同時(shí)還是下一個(gè)詞的起始符,在把day加入詞匯序列之后需要從=開(kāi)始接著分析。

第3個(gè)詞是=,第4個(gè)詞是24,第5個(gè)詞是*,第6個(gè)詞是3600,第7個(gè)詞是分號(hào);

在詞法分析時(shí)要把數(shù)字字符串24和3600轉(zhuǎn)化為整數(shù)24和3600,這兩個(gè)在程序里是不同的。

10進(jìn)制、16進(jìn)制、8進(jìn)制、2進(jìn)制、浮點(diǎn)數(shù)的支持,都是詞法分析時(shí)的任務(wù)。

另外,轉(zhuǎn)義字符串也要在這里支持。

'' 在源代碼里是字符串文本,包含著4個(gè)字符' 0 ',要轉(zhuǎn)義成單個(gè)字符0。

' ' ' ' ' '的處理和''一樣。

詞法分析還是很好寫(xiě)的。

2,語(yǔ)法分析

這是編譯器前端最難寫(xiě)的模塊,它需要把源代碼轉(zhuǎn)化成一棵描述整個(gè)程序結(jié)構(gòu)的多叉樹(shù)。

這個(gè)多叉樹(shù)叫做抽象語(yǔ)法樹(shù)(英文縮寫(xiě)AST)。

類(lèi)型、變量、運(yùn)算符、函數(shù)定義、函數(shù)調(diào)用、if語(yǔ)句、for/while循環(huán),都是這個(gè)這棵樹(shù)的一部分。

抽象語(yǔ)法樹(shù)的層次結(jié)構(gòu),與源代碼的結(jié)構(gòu)是一樣的。

如果是這樣的源代碼的話:

int sum = 0;

for (int i = 0; i < 8; i++) {

if (i % 2 == 0)

sum += i;

}

那么語(yǔ)法樹(shù)是這樣的:

f24d7d2a-12df-11ed-ba43-dac502259ad0.jpg

語(yǔ)法樹(shù)

初始化語(yǔ)句sum = 0與后續(xù)的for循環(huán)是順序執(zhí)行的,它們屬于同一個(gè)順序塊,在語(yǔ)法樹(shù)上有同一個(gè)父節(jié)點(diǎn)。

for循環(huán)有4個(gè)子節(jié)點(diǎn)初始化表達(dá)式i = 0,條件i < 8,循環(huán)體if語(yǔ)句、更新表達(dá)式i++。

其中循環(huán)體又是個(gè)if語(yǔ)句,具有2個(gè)子節(jié)點(diǎn):條件表達(dá)式i % 2 == 0,主體sum += i。

while循環(huán)的結(jié)構(gòu)與for類(lèi)似,只要去掉初始化表達(dá)式和更新表達(dá)式就行,只有2個(gè)節(jié)點(diǎn)。

把詞法分析之后的詞匯序列轉(zhuǎn)化成抽象語(yǔ)法樹(shù)時(shí),常用的方法是有限自動(dòng)機(jī)。

也可以把代碼直接寫(xiě)成遞歸函數(shù)調(diào)用,但是后續(xù)改起語(yǔ)法來(lái)就比較麻煩。

我一開(kāi)始就是把scf的parse模塊寫(xiě)成了遞歸函數(shù)調(diào)用,后來(lái)為了可以編輯語(yǔ)法,又自己做了個(gè)簡(jiǎn)單的有限自動(dòng)機(jī)。

3,語(yǔ)義分析,

把語(yǔ)法樹(shù)遍歷一遍,檢查一下類(lèi)型是否匹配,就是語(yǔ)義分析。

如果要支持面向?qū)ο?/strong>的話,就可以在這里進(jìn)行函數(shù)重載運(yùn)算符重載。

常量表達(dá)式也要在這里計(jì)算出來(lái),int day = 24 *3600要轉(zhuǎn)化成day = 86400。

f26b1600-12df-11ed-ba43-dac502259ad0.jpg

常量表達(dá)式的計(jì)算

對(duì)語(yǔ)法樹(shù)進(jìn)行遍歷時(shí),不同的語(yǔ)法節(jié)點(diǎn)使用的處理函數(shù)不同的,這就是語(yǔ)義

符號(hào)=要當(dāng)作賦值,符號(hào)+要當(dāng)作加法,其他類(lèi)似。

C語(yǔ)言里常見(jiàn)的函數(shù)調(diào)用,語(yǔ)法樹(shù)是這樣的:

int printf(const char* fmt, ...);

int main()

{

printf("hello world ");

}

函數(shù)調(diào)用也是一個(gè)運(yùn)算符,具有一個(gè)單獨(dú)的語(yǔ)法節(jié)點(diǎn),它的子節(jié)點(diǎn)都是它的參數(shù)

其中函數(shù)名也是一個(gè)參數(shù),要轉(zhuǎn)化為對(duì)應(yīng)的函數(shù)體的節(jié)點(diǎn)的指針。

通過(guò)這個(gè)指針才可以找到函數(shù)的代碼,進(jìn)行內(nèi)聯(lián)優(yōu)化(inline)。

f28dd780-12df-11ed-ba43-dac502259ad0.jpg

函數(shù)調(diào)用和定義

如果要是調(diào)用的外部函數(shù),只有聲明沒(méi)有實(shí)現(xiàn),那就沒(méi)法內(nèi)聯(lián)了。

4,中間代碼生成(三地址碼),從這里開(kāi)始就是編譯器的后端了。

這一步也是對(duì)語(yǔ)法樹(shù)進(jìn)行遍歷,把對(duì)應(yīng)的表達(dá)式、函數(shù)、if語(yǔ)句、for循環(huán)都變成類(lèi)似匯編的三地址碼。

上面那段for循環(huán),這時(shí)會(huì)被變成如下的三地址碼序列:

assign sum, 0

assign i, 0

start: // for循環(huán)的開(kāi)頭

cmp i, 8

jge end //條件不成立,則結(jié)束循環(huán)

assignt, i % 2 // t是編譯器生成的臨時(shí)變量

cmp t, 0

jne next

add sum, sum, i // 這行才是三地址碼

next: // 下一輪for循環(huán)

inc i // 循環(huán)變量+1

jmp start //跳轉(zhuǎn)回開(kāi)頭,繼續(xù)循環(huán)

end:// for循環(huán)結(jié)束

到了這里,那個(gè)復(fù)雜的樹(shù)型結(jié)構(gòu)已經(jīng)變成線形結(jié)構(gòu)了,可以按順序?qū)懙揭粋€(gè)文本文件里,這就是匯編代碼。

到這里,編譯器就可以生成類(lèi)似gcc -S的匯編代碼了。

5,中間代碼優(yōu)化,

這是編譯器后端的主要部分,屬于機(jī)器無(wú)關(guān)優(yōu)化,這部分的優(yōu)化是不依賴于CPU平臺(tái)的。

scf框架的這部分包含以下功能:

1)內(nèi)聯(lián)函數(shù),

2)有向無(wú)環(huán)圖DAG的生成,

3)帶二級(jí)指針參數(shù)的函數(shù)調(diào)用分析,

4)指針別名分析,也就是分析指針指向的變量,

5)活躍變量分析,

6)變量的加載保存分析,

7)需要自動(dòng)內(nèi)存管理的變量分析,

8)代碼流程圖的深度優(yōu)先排序,

9)自動(dòng)內(nèi)存管理代碼的添加,

10)基本塊內(nèi)的優(yōu)化,

11)循環(huán)分析,

會(huì)把一些變量盡量在循環(huán)的入口加載,在循環(huán)出口保存,減少循環(huán)內(nèi)的內(nèi)存讀寫(xiě)。

沒(méi)有常量傳播的優(yōu)化,哪天有空我把它添上

6,寄存器分配,

使用圖的著色算法,之前的文章寫(xiě)過(guò)。

7,指令選擇,

直接寫(xiě)在代碼里的,沒(méi)做龍書(shū)里提到的那個(gè)樹(shù)的覆蓋。

8,機(jī)器碼生成,

根據(jù)intel x64的手冊(cè)編寫(xiě)機(jī)器碼就行。

9,目標(biāo)文件生成,

也就是gcc -c 得到那個(gè).o文件。

Linux上的elf文件是什么樣的就怎么寫(xiě),可以參考linux的man手冊(cè)里對(duì)elf的講解。

10,可執(zhí)行文件的生成,

這是連接器的功能,它把多個(gè).o .a .so文件連接成一個(gè)可執(zhí)行文件。

這一步的代碼在scf/elf目錄,有興趣的可以看看。

連接之后的文件就可以在shell命令行里運(yùn)行了。

審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 機(jī)器碼
    +關(guān)注

    關(guān)注

    0

    文章

    12

    瀏覽量

    8287
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4671

    瀏覽量

    67765
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1602

    瀏覽量

    48895

原文標(biāo)題:編譯器的代碼架構(gòu)

文章出處:【微信號(hào):yikoulinux,微信公眾號(hào):一口Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    機(jī)器碼提取, 芯片破譯

    本帖最后由 北風(fēng)凜冽 于 2012-6-28 16:12 編輯 小弟現(xiàn)在的項(xiàng)目是要從集成電路的芯片里面提取程序的二進(jìn)制代碼出來(lái),哪位大神能提供MSP430系列和RENESAS系列芯片內(nèi)部機(jī)器碼提取的方法?跪求?。。?! {:23:}
    發(fā)表于 06-28 15:55

    求助機(jī)器碼問(wèn)題

    一段機(jī)器碼在兩種不同的單片機(jī)實(shí)現(xiàn)的功能一樣嗎?
    發(fā)表于 07-29 23:18

    如何將高級(jí)C語(yǔ)言編譯機(jī)器碼

    C編譯機(jī)器碼要通過(guò)預(yù)處理,編譯,匯編,鏈接四個(gè)步驟。這四個(gè)步驟由誰(shuí)做的呢?答案是編譯器。編譯器做的工作類(lèi)似我們IC行業(yè)里面的綜合。在IC設(shè)
    發(fā)表于 06-01 16:53

    arm7和arm9采用的指令系統(tǒng)的機(jī)器碼應(yīng)該是不同的吧

    菜鳥(niǎo)的問(wèn)題:arm7和arm9采用的指令系統(tǒng)的機(jī)器碼應(yīng)該是不同的吧?那么編譯器是怎么確定所采用的架構(gòu)的?或者說(shuō)我需要根據(jù)所采用的ARM芯片的不同而采用不同版本的編譯器嗎?
    發(fā)表于 10-27 16:17

    RealView編譯工具4.0版編譯器用戶指南

    ARM編譯器armcc是一個(gè)優(yōu)化的C和C++編譯器,它將標(biāo)準(zhǔn)C和標(biāo)準(zhǔn)C++源代碼編譯成用于基于ARM架構(gòu)的處理
    發(fā)表于 08-12 06:05

    C編譯器的設(shè)計(jì)文檔與源代碼

    C-編譯器的設(shè)計(jì)文檔與源代碼:本壓縮包包含了C-編譯器的設(shè)計(jì)文檔與源代碼,供學(xué)習(xí)參考。  整體框架. 3 詞法分析. 3᠙
    發(fā)表于 02-09 11:13 ?45次下載

    編譯器知識(shí)拾零

    機(jī)器代碼(Machine code)。 一個(gè)現(xiàn)代編譯器的主要工作流程如下: 源程序(source code)預(yù)處理(preprocessor)編
    發(fā)表于 11-07 15:44 ?0次下載

    編譯器是如何工作的_編譯器的工作過(guò)程詳解

    隨著計(jì)算機(jī)的發(fā)展,編譯器已經(jīng)發(fā)揮著十分重要的作用。本文主要介紹了編譯器的種類(lèi)、編譯器的工作原理以及編譯器工作的具體操作過(guò)程及步驟詳解。
    發(fā)表于 12-19 12:54 ?1.6w次閱讀

    MPLAB? XC8 C編譯器的架構(gòu)特性

    本視頻介紹了MPLAB? XC8 C編譯器的架構(gòu)特性。該編譯器編譯過(guò)程不同于傳統(tǒng)的編譯器,采用了一種稱為"OCG(全知
    的頭像 發(fā)表于 05-23 12:47 ?5785次閱讀
    MPLAB? XC8 C<b class='flag-5'>編譯器</b>的架構(gòu)特性

    如何對(duì)單片機(jī)的機(jī)器碼進(jìn)行反編譯代碼免費(fèi)下載

    應(yīng)一個(gè)做硬件的同事的要求,他利用其他軟件可以得到十六進(jìn)制的機(jī)器碼,希望做一個(gè)簡(jiǎn)單的軟件,可以將機(jī)器碼編譯成匯編指令。本來(lái)網(wǎng)上應(yīng)該有很多這方面的軟件。但他說(shuō)這個(gè)很特別,找不到,于是給他做了一個(gè)小軟件現(xiàn)在將
    發(fā)表于 07-17 17:38 ?11次下載
    如何對(duì)單片機(jī)的<b class='flag-5'>機(jī)器碼</b>進(jìn)行反<b class='flag-5'>編譯</b><b class='flag-5'>代碼</b>免費(fèi)下載

    華為方舟編譯器將在今年8月正式開(kāi)源

    4月份的P30系列國(guó)行發(fā)布會(huì)上,華為宣布了革命性的“方舟編譯器”,通過(guò)架構(gòu)級(jí)優(yōu)化,顯著提升性能,尤其是全程執(zhí)行機(jī)器碼,高效運(yùn)行應(yīng)用,徹底解決安卓應(yīng)用“邊解釋邊執(zhí)行”造成的低效率。
    的頭像 發(fā)表于 06-26 09:28 ?1977次閱讀

    電腦機(jī)器碼怎么修改

    在搜狗瀏覽搜索欄輸入:修改機(jī)器碼軟件下載 。然后點(diǎn)擊進(jìn)入根據(jù)個(gè)人愛(ài)好下載修改機(jī)器碼軟件。
    的頭像 發(fā)表于 08-09 15:35 ?5.8w次閱讀
    電腦<b class='flag-5'>機(jī)器碼</b>怎么修改

    CompCert編譯器目標(biāo)代碼生成機(jī)制研究綜述

    對(duì) Compcert編譯器目標(biāo)代碼生成機(jī)制進(jìn)行剖析,主要介紹其設(shè)計(jì)邏輯、翻譯過(guò)程、語(yǔ)義保持性以及代碼結(jié)構(gòu),并給出了 Compcert編譯器
    發(fā)表于 05-07 10:17 ?4次下載

    交叉編譯器安裝教程

    交叉編譯器中“交叉”的意思就是在一個(gè)架構(gòu)上編譯另外一個(gè)架構(gòu)的代碼,相當(dāng)于兩種架構(gòu)“交叉”起來(lái)了。Ubuntu 自帶的 gcc 編譯器是針對(duì) X86 架構(gòu)的,而我們現(xiàn)在要
    的頭像 發(fā)表于 09-29 09:12 ?3289次閱讀

    AI編譯器技術(shù)剖析

    隨著人工智能技術(shù)的飛速發(fā)展,AI編譯器作為一種新興的編譯技術(shù)逐漸進(jìn)入人們的視野。AI編譯器不僅具備傳統(tǒng)編譯器的功能,如將高級(jí)語(yǔ)言編寫(xiě)的源代碼
    的頭像 發(fā)表于 07-17 18:28 ?1199次閱讀