程序機(jī)器級(jí)表示

通過(guò)閱讀匯編代碼,我們能夠理解編譯器的優(yōu)化能力,并分析出代碼中潛在的低效率。

一、機(jī)器級(jí)代碼

在整個(gè)編譯過(guò)程中,編譯器會(huì)完成大部分工作,將把C提供的相對(duì)比較抽象的執(zhí)行模型表示的程序轉(zhuǎn)化為處理器執(zhí)行的非?;镜闹噶?。與目標(biāo)代碼相比,匯編代碼是可讀性更好的文本格式表示。能夠理解匯編代碼以及它是如何與原始C代碼相對(duì)應(yīng),是理解計(jì)算機(jī)如何執(zhí)行程序的關(guān)鍵一步。

對(duì)C程序員屏蔽的處理器狀態(tài)可見(jiàn)的

  • 程序計(jì)數(shù)器(%eip):表示將要執(zhí)行下一條指令在存儲(chǔ)器中的地址
  • 整數(shù)寄存器文件包含8個(gè)被命名的位置,分別存儲(chǔ)32位的值。這些寄存器可以存儲(chǔ)地址(對(duì)應(yīng)于C的指針)或整數(shù)數(shù)據(jù)。有的寄存器用來(lái)記錄某些重要的程序狀態(tài),其他寄存器用來(lái)保存臨時(shí)數(shù)據(jù)。
  • 條件寄存器保存最近執(zhí)行的算術(shù)指令狀態(tài),實(shí)現(xiàn)控制流中的條件變化,比如if或while
  • 浮點(diǎn)寄存器文件包含8個(gè)位置,用來(lái)存放浮點(diǎn)數(shù)據(jù)

(1)代碼示例

假設(shè)我們寫(xiě)了一個(gè)C代碼文件code.c,包含下面這樣的過(guò)程定義

int accum = 0;

int sum(int x, int y)
{
    int t = x + y;
    acum += t;
    return t;
}

在命令行執(zhí)行”-S“選項(xiàng),看到C編譯器產(chǎn)生的匯編代碼

unix> gcc -O2 -S code.c

編譯器產(chǎn)生一個(gè)匯編文件code.s,不做其他近一步工作
匯編代碼文件包含各種聲明

sum:
    pushl %ebp
    movl %esp,%ebp
    mov1 12(%ebp),%eax 
    addl 8(%ebp),%eax
    addl %eax,accum
    mov1 %ebp,%esp
    pop1 %ebp
    ret

上面代碼中每個(gè)縮進(jìn)去的行都對(duì)應(yīng)于一條機(jī)器指令。比如pushl 指令表示應(yīng)該將寄存器%ebp的內(nèi)容壓入棧中。

(2)訪問(wèn)信息

一個(gè)IA32中央處理單元(cpu)包含一組8位值的寄存器,這些寄存器用來(lái)存儲(chǔ)整數(shù)數(shù)據(jù)和指針。下圖顯示了8個(gè)寄存器,以%e開(kāi)頭。在過(guò)程處理中,對(duì)前三個(gè)寄存器(%eax,%ecx和%edx)的保存和恢復(fù)慣例將不同于接下來(lái)的三個(gè)寄存器(%ebx,%edi,%esi),最后兩個(gè)寄存器(%ebp和%esp)保存著指向棧中重要位置的指針,只有根據(jù)棧慣例的標(biāo)準(zhǔn)才能修改這兩個(gè)寄存器中的值

(a)操作指示符

大多數(shù)指令都有一個(gè)或多個(gè)操作數(shù),指示出執(zhí)行一個(gè)操作中藥引用的源數(shù)據(jù)值,以及放置結(jié)果的目的位置。源數(shù)據(jù)值可以以常數(shù)形式給出,或是從寄存器或存儲(chǔ)器中讀出,結(jié)果可以存放在寄存器或存儲(chǔ)器中。

各種操作數(shù)可能被分為三種類型。

  • 立即數(shù),也就是常數(shù)值。后面跟著一個(gè)整數(shù),比如-577或$0x1F
  • 寄存器,某個(gè)寄存器中的內(nèi)容
  • 存儲(chǔ)器引用,根據(jù)計(jì)算出來(lái)的地址(有效地址)訪問(wèn)某個(gè)存儲(chǔ)器位置。

有多種尋址模式:允許不同形式的存儲(chǔ)器引用。立即數(shù)偏移,基址寄存器,變之或索引寄存器,伸縮因子(必需是1、2、4、8)。

(3)數(shù)據(jù)傳送指令

最頻繁的指令是執(zhí)行數(shù)據(jù)傳送指令。操作符指令能夠完成許多機(jī)器中要好幾條指令才能完成的功能。下圖列出一些重要的數(shù)據(jù)傳送指令,最常用的是傳送雙字的movl指令。

源操作數(shù)指令一個(gè)值,它可以是立即數(shù),可以存放在寄存器中,也可以存放在存儲(chǔ)器中。目的操作數(shù)指定一個(gè)位置,它可以是寄存器,也可以是存儲(chǔ)器地址。

數(shù)據(jù)傳輸指令.jpg

第一個(gè)是原操作數(shù),第二個(gè)是目的操作數(shù)。

movb指令是類似的,除了它只傳送一個(gè)字節(jié)。movw傳送兩個(gè)字節(jié)。movsbl和movzbl指令負(fù)責(zé)拷貝一個(gè)字節(jié),并設(shè)置目的操作數(shù)中其余的位。movsbl指令的源操作數(shù)時(shí)單字節(jié)的,它執(zhí)行符號(hào)擴(kuò)展到32位(將高24位設(shè)置為源字節(jié)的最高位),然后拷貝到雙字的目的中。movzbl指令的源操作數(shù)時(shí)單字節(jié)的,在前面加24個(gè)0擴(kuò)展到32位,并將結(jié)果拷貝到雙字的目的中。

pushl和popl指令都只有一個(gè)操作數(shù)----同于壓入的數(shù)據(jù)源和用于彈出的目的數(shù)據(jù)。程序棧存放在儲(chǔ)存器中某個(gè)區(qū)域。%esp保存棧頂元素的地址

(4) 算術(shù)和邏輯操作

(a)加載有效地址

加載有效地址(leal)實(shí)際上是movl指令的變形,從存儲(chǔ)器讀數(shù)據(jù)到寄存器,實(shí)際上根本沒(méi)有引用存儲(chǔ)器。第一個(gè)操作看上去是一個(gè)寄存器引用,但該指令并不是從指定的位置讀入數(shù)據(jù),而是將有效地址寫(xiě)入到目的操作數(shù)(如寄存器)。C中&S說(shuō)明這種操作,為后面的存儲(chǔ)器引用產(chǎn)生指針。例子,如果寄存器%eax值為x,指令leal 7(%edx,%eax ,4),%eax將設(shè)置寄存器%eax的值為x,那么leal 7(%edx,%edx,4),%eax將設(shè)置%eax的值為5x+7。注意目的操作數(shù)必須是寄存器。

(b)一元和二元操作

第二類操作是一元操作,只有一個(gè)操作數(shù),即做源,也作目的。這個(gè)操作數(shù)可以是一個(gè)寄存器,也可以是一個(gè)存儲(chǔ)器位置。比如說(shuō),指令incl(%esp)會(huì)使棧頂元素加1。這種說(shuō)法讓人想起C中的加1運(yùn)算符(++)和減1運(yùn)算符(--)

第三類是二元操作,第二個(gè)操作數(shù)既是源又是目的。這種語(yǔ)法讓人想起C中像+=這樣的賦值運(yùn)算符。注意,源操作數(shù)是第一個(gè),目的操作數(shù)時(shí)第二個(gè),這是不可交換操作持有的。例如指令subl %eax,%edx使寄存器%edx的值減去%eax中的值。第一個(gè)操作可以是立即數(shù),寄存器或存儲(chǔ)器位置。第二個(gè)操作數(shù)可以是寄存器或存儲(chǔ)器圍桌。不過(guò)movl指令一樣,兩個(gè)操作數(shù)不能同時(shí)都是存儲(chǔ)器位置。

(c)位移操作

先給出位移量,然后是待位移的值,可以進(jìn)行算術(shù)或邏輯右移。移位量用單個(gè)字節(jié)編碼。位移量可以是一個(gè)立即數(shù),或者存放在單字節(jié)寄存器中%cl中。左移指令:sall,shll。兩者效果都一樣,都是將右邊填上0。右移指令sarl執(zhí)行算術(shù)移位(填上符號(hào)位
,而shrl執(zhí)行邏輯位移(填上0)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容