算术和逻辑操作
加载有效地址
指令 | 效果 | 描述 |
---|---|---|
leaq S,D | D ← & S D \leftarrow \&S D←&S | 加载有效地址 |
加载有效地址指令leaq实际上是movq指令的变形。
它的指令形式是从内存读取数据到寄存器,但实际上它根本没有引用内存。
它的第一个操作数看上去是一个内存引用,但该指令并不是从指定的位置读入数据,而是将有效地址写入到目的操作数。
这条指令可以为后续的内存引用操作产生指针。
另外,它还可以简洁地描述普通的算术操作。
例如,如果寄存器%rdx的值为x,那么指令leaq 7(%rdx, %rdx, 4), %rax %rax 将设置寄存器%rax的值为5x+7。
编译器经常发现leaq的一些灵活用法,根本就与有效地址计算无关。
目的操作数必须是一个寄存器。
一元操作
指令 | 效果 | 描述 |
---|---|---|
INC D | D ← D + 1 D \leftarrow D+1 D←D+1 | 加1 |
DEC D | D ← D − 1 D \leftarrow D - 1 D←D−1 | 减1 |
NEG D | D ← − D D \leftarrow -D D←−D | 取负 |
NOT D | D ← − D D \leftarrow -D D←−D | 取补 |
只有一个操作数,既是源又是目的。这个操作数可以是一个寄存器,也可以是一个内存位置。
上的指令是对一类指令的概括,每一类指令都可以根据数据类型的大小来进行扩展,例如,incb、incw、incl和incq。
二元操作
指令 | 效果 | 描述 |
---|---|---|
ADD S,D | D ← D + S D \leftarrow D + S D←D+S | 加 |
SUB S,D | D ← D − S D \leftarrow D - S D←D−S | 减 |
IMUL S,D | D ← D ∗ S D \leftarrow D * S D←D∗S | 乘 |
XOR S,D | D ← D ⨁ S D \leftarrow D \bigoplus S D←D⨁S | 异或 |
OR S,D | D ← D ⋁ S D \leftarrow D\bigvee S D←D⋁S | 或 |
AND S,D | D ← D ⋀ S D \leftarrow D \bigwedge S D←D⋀S | 与 |
第二个操作数,既是目的又是源。
不过,要注意,源操作数是第一个,目的操作数是第二个,对于不可交换的操作来说,这看上去很奇特。
第一个操作数可以是立即数、寄存器或者内存位置。
第二个操作数可以寄存器或者内存位置。
注意,当第二个操作数为内存地址时,处理器必须从内存读出值,执行操作,再把结果写回内存。
上述指令同样也会根据类型的不同进行扩展。
移位操作
指令 | 效果 | 描述 |
---|---|---|
SAL k,D | D ← D ≪ k D \leftarrow D \ll k D←D≪k | 左移 |
SHL k,D | D ← D ≪ k D \leftarrow D \ll k D←D≪k | 左移(等同于SAL) |
SAR k,D | D ← D ≫ k D \leftarrow D \gg k D←D≫k | 算术右移 |
SHR k,D | D ← D ≫ k D \leftarrow D \gg k D←D≫k | 逻辑右移 |
移位操作,先给出移位量,然后第二项给出的是要移位的数。
可以进行算术和逻辑右移。
移位量可以是一个立即数,或者放在单字节寄存器%cl中。这些指令很特别,因为只允许以这个特定的寄存器作为操作数。
原则上来说,1个字节的移位量使得移位量的编码范围可以达到
2
8
−
1
=
255
2^8-1=255
28−1=255。
x86-64中,移位操作对
w
w
w位长的数据值进行操作,移位量是由%cl寄存器的低
m
m
m决定的,这里
2
m
=
w
2^m=w
2m=w。高位被忽略。
左移指令有两个名字:SAL和SHL。两者的效果是一样的,都是将右边填上0。
右移指令不同,SAR执行算术移位(填上符号位),而SHR执行逻辑移位(填上0)。
移位的目的操作数可以是一个寄存器或是一个内存位置。
特殊的算术操作
当两个64位有符号或无符号整数相乘得到的乘积需要128位来表示时,这就是特殊的算术操作的使用场景。
x86-64指令集对128位(16字节)数的操作提供了有限的支持。
延续字(2字节)、双字(4字节)和四字(8字节)的命名惯例,Intel把16字节的数称为八字(oct number)。
下表列出了支持产生两个64位数字的全128位乘积以及整数除法的指令。
指令 | 效果 | 描述 |
---|---|---|
i m u l q S imulq \quad S imulqS | R [ % r d x ] : R [ % r a x ] ← S × R [ % r a x ] R[\%rdx]: R[\%rax] \leftarrow S \times R[\%rax] R[%rdx]:R[%rax]←S×R[%rax] | 有符号全乘法 |
m u l q S mulq \quad S mulqS | R [ % r a x ] : R [ % r a x ] ← S × R [ % r a x ] R[\%rax]: R[\%rax] \leftarrow S \times R[\%rax] R[%rax]:R[%rax]←S×R[%rax] | 无符号全乘法 |
c l t o clto clto | R [ % r d x ] : R [ % r a x ] ← 符 号 扩 展 ( R [ % r a x ] ) R[\%rdx]:R[\%rax] \leftarrow 符号扩展(R[\%rax]) R[%rdx]:R[%rax]←符号扩展(R[%rax]) | 转换为8字 |
i d i v q S idivq \quad S idivqS |
R
[
%
r
d
x
]
←
R
[
%
r
d
x
]
:
R
[
%
r
a
x
]
m
o
d
S
R[\%rdx] \leftarrow R[\%rdx]: R[\%rax] modS
R[%rdx]←R[%rdx]:R[%rax]modS R [ % r d x ] ← R [ % r d x ] : R [ % r a x ] ÷ S R[\%rdx]\leftarrow R[\%rdx]:R[\%rax] \div S R[%rdx]←R[%rdx]:R[%rax]÷S | 有符号除法 |
d i v q S divq \quad S divqS |
R
[
%
r
d
x
]
←
R
[
%
r
d
x
]
:
R
[
%
r
a
x
]
m
o
d
S
R[\%rdx] \leftarrow R[\%rdx]:R[\%rax] mod S
R[%rdx]←R[%rdx]:R[%rax]modS R [ % r d x ] ← R [ % r d x ] : R [ % r a x ] ÷ S R[\%rdx] \leftarrow R[\%rdx]:R[\%rax] \div S R[%rdx]←R[%rdx]:R[%rax]÷S | 无符号除法 |
关于imulq指令有两种不同的形式。
其中,一种就是前面讲到的普通的四字乘法指令,该指令是一个双操作数的指令,它从两个64位操作数产生一个64位乘积。
另一种,就是这里提到的两条单操作数乘法指令,以计算两个64位值的全128位乘积——一个无符号数乘法,另一个是补码乘法。
这两条指令都要求一个操作数必须在寄存器
%
r
a
x
\%rax
%rax中,而另一个作为指令的源操作数给出。
然后乘积放在寄存器
%
r
a
x
\%rax
%rax(高64位)和
%
r
a
x
\%rax
%rax(低64位)中。
虽然imulq这个名字可以用于两类不同的乘法操作,但是汇编能够通过计算操作数的数目,分辨出想用哪条指令。
有符号除法指令
i
d
i
v
q
idivq
idivq将寄存器
%
r
d
x
\%rdx
%rdx(高64位)和
%
r
a
x
\%rax
%rax(低64位)中的128位数作为被除数,而除数作为指令的操作数给出。
将指令的商存储在寄存器
%
r
a
x
\%rax
%rax中,将余数存储在寄存器
%
r
d
x
\%rdx
%rdx中。
对于大多数64位除法应用来说,除数也常常是一个64位的值。
这个值应该存放在
%
r
a
x
\%rax
%rax中,
%
r
d
x
\%rdx
%rdx的位应该设置为全0(无符号运算)或者
%
r
a
x
\%rax
%rax的符号为(有符号运算)。
后面这个操作可以由指令
c
q
t
o
cqto
cqto来完成。
这条指令不需要操作数——它隐含读出
%
r
a
x
\%rax
%rax的符号位,并将它复制到
%
r
d
x
\%rdx
%rdx的所有位。