何时以及为什么我们签署扩展和使用干熄焦与MUL / DIV? [英] When and why do we sign extend and use cdq with mul/div?
问题描述
我做了一个测试todayand我不明白涉及双转换为四字只是时间问题。
这让我思考,为什么/我们什么时候签延长乘除?此外,当我们使用说明像干熄焦?
你可以从一个insn参考手册(链接在的 86 标签的wiki)标签的86显示问题:
- 单操作数
MUL
/IMUL
:EDX:EAX = EAX * SRC
- 双操作数
IMUL
:DST * = SRC
-
DIV
/IDIV
:分裂EDX:EAX
由src。在商EAX
,其余的在EDX
。有没有DIV
/IDIV
的形式,忽略EDX
中输入。 -
干熄焦
登录扩展EAX
到EDX:EAX
,即广播EAX
的符号位为EDX
的每一位。不要与cdqe
,64位指令,可以做混淆MOVSX RAX,EAX
用较少的insn字节。最初(8086),只是有
CBW
(AX = SIGN_EXTEND(人)
)和CWD
(DX:AX = SIGN_EXTEND(AX)
)。 86到32位和64位扩展所做的助记符有些模棱两可(但要记住,不是CBW
其他的内,EAX版本总是以结束Ë
的延伸)。没有DL = sign_bit(人),因为8位MUL和DIV是特殊的,并使用斧
而不是DL指示:人
。
由于输入 [I] MUL
是单寄存器,你永远需要之前做 EDX
什么乘法。
如果您输入的签署,您符号扩展它来填充你使用作为输入到乘法例如寄存器与 MOVSX
或 CWDE
( EAX = SIGN_EXTEND(AX)
)。如果输入是无符号,你零扩展。 (所不同的是,如果你只需要乘法结果的低16位,例如,<一个href=\"http://stackoverflow.com/questions/34377711/which-2s-complement-integer-operations-can-be-used-without-zeroing-high-bits-in/34377712#34377712\">it如果任一方高16位或两个输入包含垃圾的无关紧要。)
对于一个鸿沟,你总是需要到零或符号扩展EAX到EDX。零延伸是一样的只是无条件零EDX,所以有它没有什么特别的指令。刚 XOR EDX,EDX
。
干熄焦
的存在,因为它比 MOV EDX,EAX
/ 特区EDX短很多31
播出EAX的符号位在EDX每一位。此外,随着移动计数> 1是不存在的,直到286,所以在8086,你需要一个循环,如果 CWD
不存在(<$的16位版本C $ C>干熄焦)。
在64位模式,符号和零扩展的32位值64是常见的。该ABI允许在64位寄存器,保持32位值的高32位的垃圾,所以如果你的函数只应该看看 EDI
的低32位,你不能只需使用 [阵列+ RDI]
索引数组。
所以你看到很多的 MOVSX RDI,EDI
(符号扩展),或 MOV EAX,EDI
(零扩展,是的这是更有效地使用不同的目标寄存器,因为英特尔MOV淘汰不符合 MOV一样,同样
)
I had a test todayand the only question I didn't understand involved converting a doubleword to a quad word.
That got me thinking, why/when do we sign extend for multiplication or division? In addition, when do we use instructions like cdq?
As you can see from the insn ref manual (link in the x86 tag wiki):
- one-operand
mul
/imul
:edx:eax = eax * src
- two-operand
imul
:dst *= src
div
/idiv
: dividesedx:eax
by the src. quotient ineax
, remainder inedx
. There's no form ofdiv
/idiv
that ignoresedx
in the input.cdq
sign-extendseax
intoedx:eax
, i.e. broadcasts the sign bit ofeax
into every bit ofedx
. Not to be confused withcdqe
, the 64bit instruction that doesmovsx rax, eax
with fewer insn bytes.Originally (8086), there was just
cbw
(ax = sign_extend(al)
) andcwd
(dx:ax = sign_extend(ax)
). The extensions of x86 to 32bit and 64bit have made the mnemonics slightly ambiguous (but remember, other thancbw
, the within-eax versions always end with ane
for Extend). There is no dl=sign_bit(al) instruction because 8bit mul and div are special, and useax
instead ofdl:al
.
Since the inputs to [i]mul
are single registers, you never need to do anything with edx
before a multiply.
If your input is signed, you sign-extend it to fill the register you're using as an input to the multiply e.g. with movsx
or cwde
(eax = sign_extend(ax)
). If your input is unsigned, you zero extend. (With the exception that if you only need the low 16 bits of the multiply result, for example, it doesn't matter if the upper 16 bits of either or both inputs contain garbage.)
For a divide, you always need to zero or sign extend eax into edx. Zero-extending is the same as just unconditionally zeroing edx, so there's no special instruction for it. Just xor edx,edx
.
cdq
exists because it's a lot shorter than mov edx, eax
/ sar edx, 31
to broadcast the sign bit of eax to every bit in edx. Also, shifts with count > 1 didn't exist until 286, so on 8086, you'd need a loop if cwd
didn't exist (the 16bit version of cdq
).
In 64bit mode, sign and zero extending 32bit values to 64bit is common. The ABI allows garbage in the high 32bits of a 64bit register holding a 32bit value, so if your function is only supposed to look at the low 32bits of edi
, you can't just use [array + rdi]
to index the array.
So you see a lot of movsx rdi, edi
(sign extend), or mov eax, edi
(zero-extend, and yes it's more efficient to use a different target register, because Intel mov-elimination doesn't work with mov same,same
)
这篇关于何时以及为什么我们签署扩展和使用干熄焦与MUL / DIV?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!