将int32
添加到64位native int
时,CLR是否对32位整数进行符号扩展或零扩展?最重要的是:根据它做出选择的信息?
我正在编写.NET编译器,并且已经完全阅读了ECMA规范,但找不到答案。
CLI在其操作中仅对存储在其评估堆栈中的值支持这些类型的子集:
int32
,int64
和native int
。
- ECMA 335,第I部分12.1:支持的数据类型
由于评估堆栈上的值没有关于其签名的信息,因此操作数签名的指令有两个变体:一个用于签名,一个用于无符号整数。只要操作数不同,add
,sub
和mul
指令(不检查溢出的指令)就不需要关心操作数的签名大小相同,因此只有一个变体。但是,操作数并不总是相同的大小...
ECMA 335,第III节1.5:操作数类型表表明int32
和native int
可以添加,减去,相乘和分割。结果再次是native int
。在64位系统上,native int
为64位宽。
ldc.i4.0 // Load int32 0
conv.i // Convert to (64-bit) native int
ldc.i4.m1 // Load int32 -1
add // Add native int 0 and int32 0xFFFFFFFF together
那么结果会是什么?请注意,根据规范,运行时不需要跟踪堆栈上值的确切类型或签名:它只知道int32
,int64
和native int
(和其他一些与此无关的其他内容。)
我认为IntPtr
和UIntPtr
算术,因为它在内部表示为本机int,也会使用这种加法。但是,ILSpy表明在C#中添加IntPtr
和Int32
会调用IntPtr
类上的重载+运算符,该类仅接受带符号的Int32
参数。
直接在CIL中执行(使用add
指令)也表示整数被解释为已签名。它也应该在Mono中实现,但我找不到任何提及我的发现的参考。
答案 0 :(得分:5)
添加相同bitsize的两个值时,签名无关紧要。例如,将32位-10(0xfffffff6
)添加到32位10(0x0000000a
)将正确地产生0.因此,CIL中只有一条add
指令(通用语言)。
但是,当添加两个不同位数的值时,签名确实很重要。例如,添加32位-10到64位10可以在无符号时生成4294967296(0x100000000
),在签名时生成0。
CIL add
指令允许添加本机整数和 32位整数。本机整数可以是64位(在64位系统上)。 测试显示add
将32位整数视为有符号整数,并对其进行符号扩展。 This is not always correct,可以将其视为a bug。微软目前无法修复它。
因为溢出检查取决于操作数是被视为无符号还是有符号,所以add.ovf
有两种变体:add.ovf
(有符号)和add.ovf.un
(无符号)。但是,当将32位整数添加到本机整数时,这些变体也正确地对扩展小的操作数进行符号扩展。
因此,根据C#的溢出检查设置,添加本机整数和无符号32位整数可能会产生不同的结果。显然,我无法弄清楚这一点是CIL语言设计中的错误或疏忽的结果。