assembly - 我们何时以及为何签署扩展并使用带有 mul/div 的 cdq?

标签 assembly x86 division integer-division sign-extension

我今天做了一个测试,唯一不明白的问题是将双字转换为四字。

这让我想到,为什么/什么时候我们为乘法或除法签署扩展?另外,我们什么时候使用cdq这样的指令?

最佳答案

使用 cdq / idiv 进行有符号 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

如果在 idivyou can get a large positive result for -5 / 2, for example 之前将 EDX/RDX 归零而不是符号扩展到 EDX:EAX 。

使用 64/32 位 => 32 位除法的“全功率”是可能的,但除非您知道除数足够大以便商不会溢出,否则不安全。 (即,您通常无法仅使用 (a*b) / c/mul 和 EDX:EAX 中的 64 位临时文件来实现 div。)

除法在商溢出时引发异常 (#DE)。在 Unix/Linux 上,the kernel delivers SIGFPE 用于算术异常,包括除法错误。使用正常符号或零扩展除法,溢出仅可能为 with idiv of INT_MIN / -1 (即最负数的 2 的补码特例。)

正如您从 insn ref 手册中看到的(链接在 标签维基中):
  • 一操作数 mul/imul : edx:eax = eax * src
  • 两个操作数 imul : dst *= src 。例如 imul ecx, esi 不读取或写入 eax 或 edx。


  • div/idiv :将 edx:eax 除以 src。商在 eax 中,余数在 edx 中。没有任何形式的 div/idiv 忽略输入中的 edx
  • cdq eax 符号扩展为 edx:eax ,即将 eax 的符号位广播到 edx 的每一位。不要与 cdqe 混淆,64 位指令是 movsxd rax, eax 的更紧凑形式。

    最初 (8086),只有 cbw ( ax = sign_extend(al) ) 和 cwd ( dx:ax = sign_extend(ax) )。 x86 到 32 位和 64 位的扩展使得助记符有点含糊不清(但请记住,除了 cbw 之外,eax 内的版本总是以 e 结尾来表示扩展)。没有 dl=sign_bit(al) 指令,因为 8bit mul 和 div 是特殊的,并且使用 ax 而不是 dl:al


  • 由于 [i]mul 的输入是单个寄存器,因此您无需在乘法之前对 edx 执行任何操作。

    如果您的输入已签名,则对其进行签名扩展以填充您用作乘法输入的寄存器,例如使用 movsxcwde ( eax = sign_extend(ax) )。如果您的输入是无符号的,则扩展为零。 (除非您只需要乘法结果的低 16 位,例如 it doesn't matter if the upper 16 bits of either or both inputs contain garbage 。)

    对于除法,您总是需要将 eax 归零或符号扩展到 edx。零扩展与无条件将 edx 归零相同,因此没有特殊说明。只是 xor edx,edx
    cdq 存在是因为它比 mov edx, eax/sar edx, 31 短得多,可以将 eax 的符号位广播到 edx 中的每一位。此外,立即计数 > 1 的移位直到 186 才存在,并且每个计数仍然是 1 个周期,因此在 8086 上,您必须做更糟糕的事情(例如分支,或将符号位旋转到底部并隔离 + neg它)。所以8086中的cwd在需要的时候节省了大量的时间/空间。

    在 64 位模式下,符号和零将 32 位值扩展到 64 位是常见的。 ABI 允许在保存 32 位值的 64 位寄存器的高 32 位中存在垃圾,因此如果您的函数只应该查看 edi 的低 32 位,则不能仅使用 [array + rdi] 来索引数组。

    所以你会看到很多 movsx rdi, edi(符号扩展)或 mov eax, edi(零扩展,是的,使用不同的目标寄存器更有效,因为英特尔 mov-elimination 不适用于 mov same,same )

    关于assembly - 我们何时以及为何签署扩展并使用带有 mul/div 的 cdq?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36464879/

    相关文章:

    assembly - 为什么 linux nasm 即使没有 16 字节堆栈对齐也能工作

    assembly - 如何正确执行 ADD/SUB 有符号或无符号整数?

    c - 在汇编中使用 fgets 时出现段错误

    x86 - 如何使用 LLVM 在某些指令后插入调用?

    c - 两个 float 的除法给出了错误的答案

    64-bit - 如何将 128 位被除数除以 64 位除数,其中被除数的位均为 1,而我只需要商的 64 LSB?

    assembly - 内部搬迁未解决

    c - gcc 内联汇编 - `add' 的操作数类型不匹配,试图创建无分支代码

    java - 去除除法中的小数

    c - 从目标文件中提取单个过程?