Assembly 我们何时以及为什么与mul/div签订扩展和使用干熄焦协议?
今天我做了一个测试,唯一不懂的问题是把一个双字转换成四字 这让我想,为什么/什么时候我们用符号扩展乘法或除法?此外,我们什么时候使用像cdq这样的指令?使用/for签名32位/32位=>32位除法,Assembly 我们何时以及为什么与mul/div签订扩展和使用干熄焦协议?,assembly,x86,division,integer-division,sign-extension,Assembly,X86,Division,Integer Division,Sign Extension,今天我做了一个测试,唯一不懂的问题是把一个双字转换成四字 这让我想,为什么/什么时候我们用符号扩展乘法或除法?此外,我们什么时候使用像cdq这样的指令?使用/for签名32位/32位=>32位除法, xor edx,edx/div用于未签名。 以EAX中的被除数开始,除数指定为DIV或IDIV的操作数 mov eax, 1234 mov ecx, 17 cdq ; EDX = signbit(EAX) idiv ecx
xor edx,edx
/div
用于未签名。
以EAX中的被除数开始,除数指定为DIV或IDIV的操作数
mov eax, 1234
mov ecx, 17
cdq ; EDX = signbit(EAX)
idiv ecx ; EAX = 1234/17 EDX = 1234%17
如果将EDX/RDX归零,而不是将符号延伸到EDX:EAX,则在idiv
之前
使用64/32位=>32位除法的“满幂”是可能的,但除非知道除数足够大,所以商不会溢出,否则就不安全。(也就是说,通常不能仅使用mul
/div
和EDX:EAX中的64位临时值来实现(a*b)/c
)
除法在商溢出时引发异常(#DE)。在Unix/Linux上,用于算术异常,包括除法错误。对于正常符号或零扩展除法,溢出仅可能发生(即2的补码是最负数的特例)
正如您可以从insn ref手册(标记wiki中的链接)中看到的:
- 一个操作数
/mul
:imul
edx:eax=eax*src
- 两个操作数
:imul
。e、 g.dst*=src
不读取或写入eax或edximul ecx、esi
/div
:用src除以idiv
。edx:eax
,eax中的商
。没有任何形式的edx中的余数
/div
忽略输入中的idiv
edx
- 符号将
扩展到eax
,即将edx:eax
的符号位广播到eax
的每一位。不要与edx
混淆,64位指令是cdqe
的一种更紧凑的形式 最初(8086),只有movsxd-rax,eax
(cbw
)和ax=sign\u extend(al)
(cwd
)。x86到32位和64位的扩展使得助记符有点模棱两可(但请记住,除了dx:ax=sign\u extend(ax)
,eax内部版本的扩展总是以cbw
结尾)。没有dl=sign_位(al)指令,因为8位mul和div是特殊的,并且使用e
而不是ax
dl:al
由于
[i]mul
的输入是单寄存器,因此在乘法之前,不需要对edx进行任何操作
如果您的输入是有符号的,您可以对其进行符号扩展,以填充用作乘法器输入的寄存器,例如使用movsx
或cwde
(eax=sign\u extend(ax)
)。如果输入是无符号的,则进行零扩展。(例外情况是,如果您只需要乘法结果的低16位,例如,)
对于除法,您始终需要将eax扩展为edx,并对其进行零或符号化。零扩展与edx的无条件零化相同,因此没有专门的说明。只需xor edx,edx
cdq
之所以存在,是因为它比mov-edx,eax
/sar-edx,31
短得多,可以将eax的符号位广播到edx中的每一位。此外,立即计数大于1的移位直到186才存在,而且每次计数仍为1个周期,因此在8086上,您必须执行更糟糕的操作(例如分支,或将符号位旋转到底部并隔离+neg
it)。因此,8086中的cwd
在需要时节省了大量的时间/空间
在64位模式下,将32位值扩展到64位的符号和零是常见的。ABI允许在64位寄存器的高32位(包含32位值)中存在垃圾,因此如果您的函数只查看edi
的低32位,则不能仅使用[array+rdi]
对数组进行索引
因此,您会看到很多movsx rdi、edi
(符号扩展)或mov eax、edi
(零扩展,是的,使用不同的目标寄存器更有效,因为Intel mov消除不适用于mov same,same
)使用/for有符号32位/32位=>32位除法,
xor edx,edx
/div
用于未签名。
以EAX中的被除数开始,除数指定为DIV或IDIV的操作数
mov eax, 1234
mov ecx, 17
cdq ; EDX = signbit(EAX)
idiv ecx ; EAX = 1234/17 EDX = 1234%17
如果将EDX/RDX归零,而不是将符号延伸到EDX:EAX,则在idiv
之前
使用64/32位=>32位除法的“满幂”是可能的,但除非知道除数足够大,所以商不会溢出,否则就不安全。(也就是说,通常不能仅使用mul
/div
和EDX:EAX中的64位临时值来实现(a*b)/c
)
除法在商溢出时引发异常(#DE)。在Unix/Linux上,用于算术异常,包括除法错误。对于正常符号或零扩展除法,溢出仅可能发生(即2的补码是最负数的特例)
正如您可以从insn ref手册(标记wiki中的链接)中看到的:
- 一个操作数
mul
/imul
:edx:eax=eax*src
- 两个操作数
imul
:dst*=src
。e、 g.imul ecx、esi
不读取或写入eax或edx
div
/idiv
:用src除以edx:eax
。eax中的商
,edx中的余数
。没有任何形式的div
/idiv
忽略输入中的edx
- 符号将
eax
扩展到edx:eax
,即将eax
的符号位广播到