C语言具有带符号和无符号类型,例如char和int。我不确定如何在汇编级别上实现它,例如,在我看来有符号和无符号的乘法会带来不同的结果,所以汇编同时执行无符号和有符号算术还是仅执行一个,这在某种程度上被仿真不同的情况?
如果您查看x86的各种乘法指令,仅查看32位变量而忽略BMI2,则会发现以下内容:
imul r/m32
imul r32, r/m32
imul r32, r/m32, imm
mul r/m32
请注意,只有“加宽”乘法具有无符号的对应项。中间的两个带有星号的形式都是有符号和无符号乘法,因为对于这种情况,如果您没有多余的“上部”, 那是同一回事 。
“加宽”乘法在C语言中没有直接等效项,但是编译器仍然可以(而且经常)使用这些形式。
例如,如果您编译此代码:
uint32_t test(uint32_t a, uint32_t b) { return a * b; } int32_t test(int32_t a, int32_t b) { return a * b; }
使用GCC或其他相对合理的编译器,您将获得以下内容:
test(unsigned int, unsigned int): mov eax, edi imul eax, esi ret test(int, int): mov eax, edi imul eax, esi ret
(带有-O1的实际GCC输出)
因此,有符号性与乘法(至少与您在C语言中使用的乘法类型无关)和其他一些操作无关紧要,即:
x86不会为此提供单独的签名/未签名版本,因为无论如何都没有区别。
但是对于某些操作,则有所不同,例如:
idiv
div
sar
shr
但是最后一个是特殊的,x86对此没有签名也没有签名的版本,而是有一个操作(cmp,实际上只是一个非破坏性的sub)同时执行,并且给出了多个结果( “标记”受到影响)。稍后的说明会实际使用这些标志(分支,有条件的移动等setcc),然后选择它们关心的标志。例如
cmp
sub
setcc
cmp a, b jg somewhere
somewhere如果a“签名大于” 将继续b。
somewhere
a
b
cmp a, b jb somewhere
somewhere如果a是“ unsigned below”,则将走b。
这不是正式的证明有符号和无符号乘法是相同的,我将尽力让您了解为什么它们应该相同。
考虑4位2的补码整数。它们的各个位的权重是从lsb到msb,1、2、4和-8。当您将这些数字中的两个相乘时,可以将其中一个分解为对应于其位的4个部分,例如:
0011 (decompose this one to keep it interesting) 0010 ---- * 0010 (from the bit with weight 1) 0100 (from the bit with weight 2, so shifted left 1) ---- + 0110
2 * 3 = 6,所以一切都检查完了。这只是大多数人在学校学习的常规的长整数乘法,只有二进制数,这使它变得容易得多,因为您不必乘以十进制数字,只需乘以0或1并进行移位即可。
无论如何,现在取一个负数。符号位的权重为-8,因此在某一点上您将得到部分乘积-8 * something。与8的乘积向左移动3,因此以前的lsb现在为msb,所有其他位均为0。现在,如果您将其取反(毕竟是-8,而不是8),则什么都不会发生。零显然是不变的,但8也是不变的,并且通常只有msb设置的数字是不变的:
-8 * something
-1000 = ~1000 + 1 = 0111 + 1 = 1000
因此,如果msb的权重为8(如无符号情况)而不是-8,则您将执行相同的操作。