1.算術和邏輯操作類指令分四類:加載有效地址,一元操作,二元操作和移位,如下:

2. leaq指令,類似mov指令,它左側的數看似是給出一個地址,在內存中從給定的地址取操作數,傳給右邊的目的地。但其實沒有取,而是直接將左側的數對應的地址傳給了右側的目的地。
例子: leaq 7(%rdx,%rdx,4),%rax
若%rdx的值為x,則最后%rax的值為5x+7,而不是以5x+7為地址,在內存中尋址得到的操作數
3. 第二組是一元指令,目的地為寄存器或一個內存位置,具體不用介紹,看表就好。
4. 第三組是二元指令,第二個操作數既是源又是目的,第一個操作數可以為立即數、寄存器或內存位置,第二個操作數可以為寄存器或內存位置。
5. 第四組是移位操作,第一個操作數是移位量,它可以是立即數或存放在%cl中,第二個操作數是要移位的數,可以是寄存器或者內存位置。當移位量存放在%cl中時,具體的移位量由移位指令的移位量級來決定,比如salb,此時23=8,則移位量由%cl的低3位決定,如%cl=0cFF,則移位量為7,同理,salw移15位,sall移31位,salq移63位。而決定移位指令的移位量級的則是被移位的數的量級,比如被移位的是%rax,則用salq。
左移指令有sal和shl,兩者等價,移動后右邊填0。右移指令有sar和shr,sar為算術移位,左補符號位,shr為邏輯移位,左補0。
練習:
xorq %rdx,%rdx
1.它做了什么?
答:它實現了對%rdx的置0
2.它有什么其它的匯編表達形式?
答:movq $0,%rdx
3.比較這兩種操作分別編碼的字節長度
答:xorq占3個字節,movq這里占7個字節,所以一般使用xorq來實現置0操作,由於任何更新低位4字節的指令都會把高位4字節置為0,
所以可以用 xorl %edx,%edx(2字節) 和 movl $0,edx(5字節)來實現。
6.特殊的算術操作
1.imulq,前面提到的imulq會從兩個64位的操作數產生一個128位的操作數,截取其中的64位作為結果。此時需要兩個操作數。
(注意,一個a位的操作數和一個b位的操作數,產生的乘積是a+b位的操作數)
2.此外,x86-64提供一系列單操作數指令,用於進行全128位運算
mulq是無符號乘法,imulq是補碼乘法,它們都是單操作數指令,另一個操作數必須存在%rax中,乘積放在%rdx(高64位)和%rax(低64位)中。
測試:
#include <inttypes.h>
typedef unsigned __int128 uint128_t;
void store_uprod(uint128_t *dest,uint64_t x,uint64_t y)
{
*dest = x*(uint128_t)y;
}
此程序中,uint64_t類型由頭文件inttypes.h定義,但此文件未定義128位的整數,因此使用gcc自帶的unsigned __int128整數支持,為了和uint64_t保持一樣的命名規律,用typedef聲明成uint128_t。
其對應的匯編代碼如下:
store_uprod:
movq %rsi,%rax
mulq %rdx
movq %rax,(%rdi)
movq %rdx,8(%rdi)
ret
這里把乘積的低8字節存在了%rdi尋址的內存地址,高8字節存在了更大的地址,所以這里是小端法存儲。
3.同樣的,也有對應的單操作數無符號除法(divq)和有符號除法(idivq),即128位除法,注意沒有雙操作數的除法。
它們把%rdx(高64位)和%rax(低64位)合起來的128位作為被除數,除數則作為單操作數被給出,結果的商存在%rax中,余數存在%rdx中。
但是,當要進行64位除法時,此時沒有雙操作數的除法,怎么辦?
此時會把64位的被除數存在%rax中,%rdx被設置為全0(無符號運算)或%rax的符號位(有符號運算)。設置%rdx為%rax的符號位可以用指令cqto完成,
它不需要操作數,會自動讀出%rax的符號位,並復制到%rdx的所有位。
完畢!
