程序的機(jī)器級(jí)表示--條件碼
條件碼
條件碼表
| 標(biāo)志 | 含義 | 描述 |
|---|---|---|
| CF | 進(jìn)位標(biāo)志(Carry Flag) | 最近的操作是最高位產(chǎn)生了進(jìn)位。可用來檢測無符號(hào)操作的溢出。 |
| ZF | 零標(biāo)志(Zero Flag) | 最近的操作產(chǎn)生了0。 |
| SF | 符號(hào)標(biāo)志(Signal Flag) | 最近的操作得到的結(jié)果為負(fù)數(shù)。 |
| OF | 溢出標(biāo)志(Overflow Flag) | 最近的操作導(dǎo)致一個(gè)補(bǔ)碼的溢出。 |
leap(地址加載)指令不改變?nèi)魏螚l件碼,用來進(jìn)行地址計(jì)算。
運(yùn)算指令改變寄存器同時(shí)也會(huì)設(shè)置條件碼。
例如:
XOR,進(jìn)位與溢出標(biāo)志會(huì)設(shè)置成0。
移位操作會(huì)將進(jìn)位標(biāo)志設(shè)置為最后一個(gè)被移出的位,而溢出標(biāo)志設(shè)為0。
INC和DEC指令會(huì)設(shè)置溢出和零標(biāo)志,但是不會(huì)改變進(jìn)位標(biāo)志。
比較與測試指令
| 指令 | 基于 | 描述 |
|---|---|---|
| CMP S2,S1<br />cmpb<br />cmpw<br />cmpl<br />cmpq | S2-S1 | 比較<br />比較字節(jié)<br />比較字<br />比較雙字<br />比較四字 |
| Test S1,S2<br />testb<br />testw<br />testl<br />testq | S1&S2 | 測試<br />測試字節(jié)<br />測試字<br />測試雙字<br />測試四字 |
上表中的兩類指令(8,16,24,36位形式)只設(shè)置條件碼,不改變?nèi)魏渭拇嫫鳌@鏑MP指令是通過兩個(gè)操作數(shù)之差來設(shè)置條件碼,除了不更新寄存器之外CMP的操作與Sub的操作是一樣的,而Test與And指令是一樣的。
條件碼的訪問與使用
使用方法有三種:
-
根據(jù)條件碼的某種組合,將一個(gè)字節(jié)設(shè)置位0或1(SET指令)。SET指令的目的操作數(shù)是低位單字節(jié)寄存器元素,或是一個(gè)字節(jié)的內(nèi)存位置,指令將其設(shè)置0或1,同時(shí)對(duì)高位清零。
指令 同義名 效果 設(shè)置條件 sete D setz D<-ZF 相等/零 setne D setnz D <- ~ZF 不等/非零 sets D D <- SF 負(fù)數(shù) setns D D <- ~SF 非負(fù)數(shù) setg D setnle D <- ~(SF ^ OF)&~ZF 大于(有符號(hào)>) setge D setnl D <- ~(SF^OF) 大于等于(有符號(hào)>=) setl D setnge D <- SF^OF 小于(有符號(hào)<) setle D setng D <- (SF^OF)|ZF 小等于(有符號(hào)<=) seta D setnbe D <- CF&ZF 超過(無符號(hào)>) setae D setnb D <- ~CF 超過或相等(無符號(hào)>=) setb D setnae D <- CF 低于(無符號(hào)<) setbe D setna D <- CF|ZF 低于或相等(無符號(hào)<=) set指令的操作流程:
- 執(zhí)行比較指令
- 根據(jù)計(jì)算t=a-b設(shè)置
int comp(data_t a,data_t b)
a in %rdi, b in %rsi
comp:
cmpq %rsi, %rdi Compare a :b
setl %al Set Low-order byte of %eax to 0 or 1
movezbl %al, %eax Clear rest of %eax(and rest of %rax)
ret
同c語言不同,機(jī)器代碼不會(huì)將每個(gè)程序值都和一個(gè)數(shù)據(jù)類型聯(lián)系起來。機(jī)器代碼大部分對(duì)有符號(hào)和無符號(hào)的兩種情況使用一樣的指令。
跳轉(zhuǎn)到某部分代碼
有條件的傳送數(shù)據(jù)
程序的跳轉(zhuǎn)
正常程序是順序執(zhí)行,但是跳轉(zhuǎn)指令會(huì)跳轉(zhuǎn)另一段代碼的位置。類似c語言里面的goto,跳轉(zhuǎn)后的位置會(huì)用label指明。
跳轉(zhuǎn)主要分:
- 直接跳轉(zhuǎn):跳轉(zhuǎn)目標(biāo)是作為指令的一部分編碼的。
- 間接跳轉(zhuǎn):跳轉(zhuǎn)目標(biāo)是從內(nèi)存或者寄存器中讀出的。間接跳轉(zhuǎn)的寫法是*后面跟一個(gè)操作指示符,例如jmp*%rax就是從rax寄存器中的內(nèi)容作為跳轉(zhuǎn)目標(biāo),jmp *(%rax)即以%rax中的值作為讀地址,從內(nèi)存中讀出跳轉(zhuǎn)目標(biāo)。
jmp指令集
| 指令 | 同義名 | 跳轉(zhuǎn)條件 | 描述 |
|---|---|---|---|
| jmp Label | 1 | 直接跳轉(zhuǎn) | |
| jmp *Operand | 1 | 間接跳轉(zhuǎn) | |
| de Label | jz | ZF | 相等/零 |
| jne Label | jnz | ~ZF | 不相等/非零 |
| js Label | SF | 負(fù)數(shù) | |
| jns Label | ~SF | 非負(fù)數(shù) | |
| jg Label | jnle | (SF^OF)&ZF | 大于(有符號(hào)>) |
| jge Label | jnl | ~(SF^OF) | 大于或等于(有符號(hào)>=) |
| jl Label | jnge | SF^OF | 小于(有符號(hào)<) |
| jle Label | jng | (SF^OF)|ZF | 小于或等于(有符號(hào)<=) |
| ja Label | jnbe | CF&ZF | 超過(無符號(hào)>) |
| jae Label | jnb | ~CF | 超過或相等(無符號(hào)>=) |
| jb Label | jnae | CF | 低于(無符號(hào)<) |
| jbe Label | jna | CF|ZF | 低于或相等(無符號(hào)<=) |
上表中的跳轉(zhuǎn)指令都是有條件的--它們根據(jù)條件碼的組合,或者跳轉(zhuǎn),或者繼續(xù)執(zhí)行代碼序列中下一跳指令。條件跳轉(zhuǎn)只能是直接跳轉(zhuǎn)。
跳轉(zhuǎn)指令的編碼分為PC絕對(duì)與PC相對(duì)。
PC相對(duì):目標(biāo)指令與緊跟在跳轉(zhuǎn)指令后面那條指令的地址之間的差作為編碼。
0: 48 89 f8 mov %rdi,%rax
3: eb 03 jmp 8 <loop+0x8>
5: 48 d1 f8 sar %rax
8: 48 85 c0 test %rax, %rax
b: 7f f8 jg 5 <loop+0x5>
d: f3 c3 repz retq
movq %rdi,%rax
jmp .L2
.L3:
sarq %rax
.L2:
testq %rax
jg .L3
rep;ret
第2行下一條指令的位置是5,相對(duì)位置是3,5+3=8,即跳轉(zhuǎn)到8的位置上。
rep與repz除了在AMD上運(yùn)行的更壞之外,不會(huì)改變代碼的其他行為,不用管。
PC絕對(duì):用4個(gè)字節(jié)直接指定目標(biāo)。
條件分支
條件表達(dá)式和語句從c語言翻譯成機(jī)器代碼,最常用的是結(jié)合有條件和無條件跳轉(zhuǎn)。條件跳轉(zhuǎn)主要有兩種實(shí)現(xiàn)方式:
- 控制的條件轉(zhuǎn)移
- 數(shù)據(jù)的條件轉(zhuǎn)移
控制的條件轉(zhuǎn)移
c語言里面的if-else語句如下:
if (condition)
then-statement;
else
else-statement;
翻譯成匯編會(huì)先上面語句轉(zhuǎn)換成以下形式:
t = condition;
if (!t)
goto false;
then-statement;
goto done;
false:
else-statement;
done:
再轉(zhuǎn)換為匯編形式。
舉個(gè)例子:
c版本:
long func(long x,long y){
long result;
if (x < y)
result = y - x;
else{
result = x - y;
}
return result;
}
轉(zhuǎn)換后的版本:
long func(long x, long y){
long result;
if (x >= y)
goto x_ge_y;
result = y - x;
return result;
x_ge_y:
result = x - y;
return result;
}
匯編版本:
long func(long x, long y)
x in %rdi, y in %rsi
func:
cmpq %rsi,%rdi
jge .L2
movq %rsi, %rax
subq %rdi, %rax
ret
.L2
movq %rdi, %rax
subq %rsi, %rax
ret
數(shù)據(jù)的條件分支
處理器通過流水線獲取高性能,一條指令要經(jīng)過一些列階段,每個(gè)階段執(zhí)行所需操作的一部分(例如,從內(nèi)存中取指令,確定指令類型,從旁那個(gè)內(nèi)存讀數(shù)據(jù),執(zhí)行算數(shù)運(yùn)算,想內(nèi)存寫數(shù)據(jù),以及更新程序計(jì)數(shù)器)。這種方法通過重疊連續(xù)指令的步驟來獲取高性能,例如,在取一條指令的同時(shí),執(zhí)行前一條指令的計(jì)算。處理器需要事先確定要執(zhí)行的指令序列才能使流水線中保持充滿待執(zhí)行的指令。當(dāng)遇到調(diào)制分支的時(shí)候,只要分支條件求知完成后才能決定分支的走向。現(xiàn)代處理器采用非常精密的分支預(yù)測邏輯來預(yù)測走向,但是只要預(yù)測錯(cuò)誤就得丟棄為預(yù)測的指令所準(zhǔn)備的工作,再從正確的位置重新加載,這樣一個(gè)錯(cuò)誤會(huì)導(dǎo)致程序性能嚴(yán)重下降。只要控制流不依賴數(shù)據(jù),才能使流水線一直是滿的。
下表列舉了一些可用的條件傳送指令。每個(gè)指令有兩個(gè)操作數(shù):源寄存器或者內(nèi)存地址S,和目的寄存器R。指令的結(jié)果取決于條件碼的值,只有在條件滿足的時(shí)候,源寄存器的值才會(huì)被復(fù)制到目的寄存器中。
| 指令 | 同義名 | 傳送條件 | 描述 |
|---|---|---|---|
| cmove S, R | cmovz | ZF | 相等/零 |
| cmovne S, R | cmovnz | ~ZF | 不相等/非零 |
| cmovs S, R | SF | 負(fù)數(shù) | |
| cmovns S, R | ~SF | 非負(fù)數(shù) | |
| cmovg S, R | cmovnle | (SF^OF)&ZF | 大于(有符號(hào)) |
| cmovge S, R | cmovnl | ~(SF^OF) | 大等于(有符號(hào)) |
| cmovl S, R | cmovnge | SF^OF | 小于(有符號(hào)) |
| cmovle S, R | cmovng | (SF^OF)|ZF | 小等于(有符號(hào)) |
| cmova S, R | cmovnbe | CF&ZF | 超過(無符號(hào)) |
| cmovae S, R | cmovnb | ~CF | 超過或相等(無符號(hào)) |
| cmovb S, R | cmovnae | CF | 低于(無符號(hào)) |
| cmovbe S, R | cmovna | CF|ZF | 低于或等于(無符號(hào)) |
同條件跳轉(zhuǎn)不同,數(shù)據(jù)跳轉(zhuǎn)無需預(yù)測測試的結(jié)果就可以執(zhí)行條件傳送。
- 處理器只是讀源值(可能從內(nèi)存)
- 檢查條件碼
- 要么更新目的寄存器,要么不變
數(shù)據(jù)傳送表達(dá)式v = a ? b : c會(huì)被轉(zhuǎn)換為類似以下表達(dá)式:
v = then-statement;
ve = else-statement;
t = condition;
if (!t) v = ve;
但是條件數(shù)據(jù)傳送也不是萬能的,上面兩條語句只要一條產(chǎn)生panic就會(huì)導(dǎo)致程序崩潰。并且也不總是提高代碼的效率,比如上述兩個(gè)表達(dá)式計(jì)算量比較大的時(shí)候,條件不滿足就白計(jì)算了,甚至代價(jià)比預(yù)測錯(cuò)分支還大,條件數(shù)據(jù)傳送還是比較受限制的情況下好用。
循環(huán)
循環(huán)主要基于兩種形式:
- do-while循環(huán)。
- while循環(huán)(while內(nèi)部也有以下兩種寫法)
- while
- for
do-while循環(huán)
do-while的通用寫法如下:
do
body
while(condition)
翻譯成偽代碼就是以下形式:
loop:
body;
t = condition;
if (t)
goto loop;
舉個(gè)例子,有以下c函數(shù):
long func(long n){
long result = 1;
do{
result *= n;
n--;
}while(n > 1);
return result;
}
翻譯成等價(jià)無條件跳轉(zhuǎn)+有條件跳轉(zhuǎn)的形式:
long func(long n){
long result = 1;
loop:
result *= n;
n--;
if (n > 1)
goto loop;
return result;
}
轉(zhuǎn)換成匯編:
long func(long n)
n in %rdi
fact_do:
movl $1, %eax
.L2:
imuq %rdi, %rax
subq $1, %rdi
cmpq $1, %rdi
jg .L2
rep;ret
while循環(huán)
通用形式如下:
while(condition)
body;
轉(zhuǎn)換后形式如下:
goto test;
loop:
body;
test:
t = condition;
if (t)
goto loop;
可以看到與do-while不同的是,第一次條件判斷如果沒通過,程序就直接跳出循環(huán)。
用do-while循環(huán)同一個(gè)例子舉例。
c形式:
long func(long n){
long result = 1;
while(n > 1);{
result *= n;
n--;
}
return result;
}
翻譯成goto后的形式:
long func(long n){
long result = 1;
goto test;
loop:
result *= n;
n--;
test:
if (n > 1)
goto loop;
return result;
}
轉(zhuǎn)換為匯編的形式:
long func(long n)
n in %rdi
fact_while:
movl $1, %eax
jmp .L5
.L6:
imuq %rdi, %rax
subq $1, %rdi
.L5:
cmpq $1, %rdi
jg .L2
rep;ret
還有一種翻譯方法,當(dāng)gcc使用o1優(yōu)化等級(jí)的時(shí)候,會(huì)先轉(zhuǎn)換成do-while的形式再轉(zhuǎn)成匯編:
long func(long n){
long result = 1;
if(n <= 1)
goto done;
loop:
result *= n;
n--;
if (n != 1)
goto loop;
done:
return result;
}
匯編版本:
long func(long n)
n in %rdi
fact_while:
cmpq $1, %rdi
jle .L7
movl $1, %eax
.L6:
imuq %rdi, %rax
subq $1, %rdi
cmpq $1, %rdi
jne .L6
rep;ret
.L7:
movl $1, %eax
rep;ret
for循環(huán)
for循環(huán)其實(shí)是while的一種翻譯形式,拿上面的例子舉例:
long func(long n){
long i;
long result = 1;
for(i = 2;i <= n;i++){
result *= i;
}
return result;
}
翻譯成while后的形式:
long func(long n){
long i = 2;
long result = 1;
while(i <= n);{
result *= i;
i++;
}
return result;
}