Assembly 我们何时以及为什么与mul/div签订扩展和使用干熄焦协议?

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

今天我做了一个测试,唯一不懂的问题是把一个双字转换成四字

这让我想,为什么/什么时候我们用符号扩展乘法或除法?此外,我们什么时候使用像cdq这样的指令?

使用/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
    的符号位广播到
    edx
    的每一位。不要与
    cdqe
    混淆,64位指令是
    movsxd-rax,eax
    的一种更紧凑的形式

    最初(8086),只有
    cbw
    ax=sign\u extend(al)
    )和
    cwd
    dx:ax=sign\u extend(ax)
    )。x86到32位和64位的扩展使得助记符有点模棱两可(但请记住,除了
    cbw
    ,eax内部版本的扩展总是以
    e
    结尾)。没有dl=sign_位(al)指令,因为8位mul和div是特殊的,并且使用
    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
    的符号位广播到