CSAPP閱讀筆記-程序的機(jī)器表示--流程控制--條件碼

程序的機(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è)置條件碼。

例如:

  1. XOR,進(jìn)位與溢出標(biāo)志會(huì)設(shè)置成0。

  2. 移位操作會(huì)將進(jìn)位標(biāo)志設(shè)置為最后一個(gè)被移出的位,而溢出標(biāo)志設(shè)為0。

  3. 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指令是一樣的。

條件碼的訪問與使用

使用方法有三種:

  1. 根據(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指令的操作流程:

    1. 執(zhí)行比較指令
    2. 根據(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)的兩種情況使用一樣的指令。

  1. 跳轉(zhuǎn)到某部分代碼

  2. 有條件的傳送數(shù)據(jù)

程序的跳轉(zhuǎn)

正常程序是順序執(zhí)行,但是跳轉(zhuǎn)指令會(huì)跳轉(zhuǎn)另一段代碼的位置。類似c語言里面的goto,跳轉(zhuǎn)后的位置會(huì)用label指明。

跳轉(zhuǎn)主要分:

  1. 直接跳轉(zhuǎn):跳轉(zhuǎn)目標(biāo)是作為指令的一部分編碼的。
  2. 間接跳轉(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)方式:

  1. 控制的條件轉(zhuǎn)移
  2. 數(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í)行條件傳送。

  1. 處理器只是讀源值(可能從內(nèi)存)
  2. 檢查條件碼
  3. 要么更新目的寄存器,要么不變

數(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)主要基于兩種形式:

  1. do-while循環(huán)。
  2. while循環(huán)(while內(nèi)部也有以下兩種寫法)
    1. while
    2. 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;
}
最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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