一、算術(shù)和邏輯操作
下圖列出了x86-64的一些整數(shù)和邏輯操作抹腿。大多數(shù)操作都分成了指令類岛请。例如 add 指令類由四條加法指令組成:addb、addw警绩、addl崇败、addq,分別是字節(jié)加法房蝉、字加法僚匆、雙字加法、四字加法搭幻。事實(shí)上咧擂,不僅是 add 指令,下圖的每個(gè)指令類都有對(duì)這四種不同大小數(shù)據(jù)的指令檀蹋。分別為:加載有效地址松申、一元操作、二元操作和移位俯逾。二元操作有兩個(gè)操作數(shù)贸桶,一元操作有一個(gè)操作數(shù)。
加載有效地址:leaq 指令是 movq 指令的變形桌肴,指令形式是從內(nèi)存讀數(shù)據(jù)到寄存器皇筛,但實(shí)際上沒有引用內(nèi)存。它可以簡(jiǎn)單描述普通的計(jì)算操作坠七。編譯器經(jīng)常發(fā)現(xiàn) leaq 的一些靈活用法水醋,根本就與有效地址計(jì)算無(wú)關(guān)旗笔。例如:leaq a(b, c, d), %rax 先計(jì)算地址a + b + c * d,然后把最終地址載到寄存器rax中拄踪。
一元和二元操作:第二組中的操作是一元操作蝇恶,只有一個(gè)操作數(shù),既是源端又是目的端惶桐。這個(gè)操作數(shù)可以是一個(gè)寄存器也可以是一個(gè)內(nèi)存撮弧。第三組是二元操作,其中第二個(gè)操作數(shù)既是源又是目的姚糊。
移位操作:最后一組是移位操作贿衍,先給出移位量,然后第二項(xiàng)給出要移位的數(shù)叛拷∩喑可以進(jìn)行算術(shù)和邏輯右移。
特殊算術(shù)操作:兩個(gè)64位有符號(hào)或無(wú)符號(hào)整數(shù)相乘得到的乘積需要128位來(lái)表示忿薇。x86-64指令集對(duì)128位數(shù)的操作提供有限的支持裙椭。下圖描述的是支持產(chǎn)生兩個(gè)64位數(shù)字的全 128位乘積以及整數(shù)除法的指令。
二署浩、控制
目前為止我們只考慮了直線代碼的行為揉燃,也就是一條命令接著一條命令順序地執(zhí)行。C語(yǔ)言中要求有條件的執(zhí)行筋栋,根據(jù)測(cè)試數(shù)據(jù)結(jié)果來(lái)決定操作執(zhí)行的順序炊汤。機(jī)器代碼提供兩種低級(jí)機(jī)制來(lái)實(shí)現(xiàn)有條件的行為:測(cè)試數(shù)據(jù)值然后根據(jù)測(cè)試結(jié)果來(lái)改變控制流或者數(shù)據(jù)流。我們先來(lái)介紹與數(shù)據(jù)相關(guān)的控制流弊攘。
1抢腐、條件碼:除了整數(shù)寄存器還有以單個(gè)位的條件碼寄存器。它們描述了最近的算術(shù)或邏輯操作的屬性襟交÷醣叮可以檢測(cè)這些寄存器來(lái)執(zhí)行條件分支指令。當(dāng)有算術(shù)與邏輯操作發(fā)生時(shí)捣域,這些條件碼寄存器當(dāng)中的值會(huì)相應(yīng)的發(fā)生變化啼染。
CF:進(jìn)位標(biāo)志寄存器。最近的操作是最高位產(chǎn)生了進(jìn)位焕梅。它可以記錄無(wú)符號(hào)操作的溢出迹鹅,當(dāng)溢出時(shí)會(huì)被設(shè)為1。
ZF:零標(biāo)志寄存器贞言,最近的操作得出的結(jié)果為0斜棚。當(dāng)計(jì)算結(jié)果為0時(shí)將會(huì)被設(shè)為1。
SF:符號(hào)標(biāo)志寄存器,最近的操作得到的結(jié)果為負(fù)數(shù)弟蚀。當(dāng)計(jì)算結(jié)果為負(fù)數(shù)時(shí)會(huì)被設(shè)為1脂新。
OF:溢出標(biāo)志寄存器,最近的操作導(dǎo)致一個(gè)補(bǔ)碼溢出(正溢出或負(fù)溢出)粗梭。當(dāng)計(jì)算結(jié)果導(dǎo)致了補(bǔ)碼溢出時(shí),會(huì)被設(shè)為1级零。
從上面可以看出断医,CF和OF可以判斷有符號(hào)和補(bǔ)碼的溢出,ZF判斷結(jié)果是否為0奏纪,SF判斷結(jié)果的符號(hào)鉴嗤。這是底層機(jī)器的設(shè)定,而編程用的高級(jí)語(yǔ)言(比如C序调,Java)就是靠這四個(gè)寄存器醉锅,演化出各種各樣的流程控制。
2发绢、訪問條件碼
對(duì)于普通寄存器來(lái)講硬耍,使用的時(shí)候一般是直接讀取它的值,而對(duì)于條件碼边酒,通常不會(huì)直接讀取经柴。常用的有如下三種方法:可以根據(jù)條件碼寄存器的某個(gè)組合,將一個(gè)字節(jié)設(shè)置為0或1墩朦∨魅希可以直接條件跳轉(zhuǎn)到程序的某個(gè)其它的部分∶セ粒可以有條件的傳送數(shù)據(jù)牛哺。
對(duì)于第一種情況,下圖描述的指令便是根據(jù)條件碼的某個(gè)組合劳吠,將一個(gè)字節(jié)設(shè)置為0或1引润,這一整類指令稱為 SET 指令,它們的區(qū)別就在與它們考慮的條件碼的組合是什么赴背,這些指令名字的不同后綴指明了它們所考慮的條件碼的組合椰拒。這些指令的后綴表示不同的條件而不是操作數(shù)的大小。比如指令 setl 和 setb 表示 “小于時(shí)設(shè)置(set less)”和“低于時(shí)設(shè)置(set below)”凰荚,而不是“設(shè)置長(zhǎng)字(set long word)”和“設(shè)置字節(jié)(set byte)”
3燃观、跳轉(zhuǎn)指令
正常情況下,指令會(huì)按照他們出現(xiàn)的順序一條一條地執(zhí)行便瑟。而跳轉(zhuǎn)指令(jump)會(huì)導(dǎo)致執(zhí)行切換到程序中一個(gè)全新的位置缆毁,我們可以理解為方法或者函數(shù)的調(diào)用。在匯編代碼中到涂,這些跳轉(zhuǎn)的目的地通常用一個(gè)標(biāo)號(hào)(label)指明脊框。
指令 jmp .L1 會(huì)導(dǎo)致程序跳過movq指令颁督,而從popq指令開始繼續(xù)執(zhí)行。在產(chǎn)生目標(biāo)代碼文件時(shí)浇雹,匯編器會(huì)確定帶標(biāo)號(hào)指令的地址沉御。并將跳轉(zhuǎn)目標(biāo)編碼為跳轉(zhuǎn)指令的一部分。昭灵、
①直接跳轉(zhuǎn):跳轉(zhuǎn)目標(biāo)是作為指令的一部分編碼的吠裆,比如上面的直接給一個(gè)標(biāo)號(hào)作為跳轉(zhuǎn)目標(biāo)
②間接跳轉(zhuǎn):跳轉(zhuǎn)目標(biāo)是從寄存器或者存儲(chǔ)器位置中讀出的,比如 jmp *%eax 表示用寄存器 %eax 中的值作為跳轉(zhuǎn)目標(biāo)烂完;再比如 jmp *(%eax) 以 %eax 中的值作為讀地址试疙,從存儲(chǔ)器中讀取跳轉(zhuǎn)目標(biāo)。
③其他條件跳轉(zhuǎn):根據(jù)條件碼的某個(gè)組合抠蚣,或者跳轉(zhuǎn)祝旷,或者繼續(xù)執(zhí)行代碼序列中的下一條指令。
4嘶窄、循環(huán)
C 語(yǔ)言提供了多種循環(huán)結(jié)構(gòu)怀跛,比如 do-while、while和for护侮。匯編中沒有相應(yīng)的指令存在敌完,我們可以用條件測(cè)試和跳轉(zhuǎn)指令組合起來(lái)實(shí)現(xiàn)循環(huán)的效果。而大多數(shù)匯編器會(huì)根據(jù)一個(gè)循環(huán)的do-while 循環(huán)形式來(lái)產(chǎn)生循環(huán)代碼羊初,即其他的循環(huán)一般也會(huì)先轉(zhuǎn)換成 do-while 形式滨溉,然后在編譯成機(jī)器代碼。
比如如下 do-while 循環(huán):