将int32添加到64位本机int的结果?

时间:2013-01-07 01:27:36

标签: .net integer clr specifications

int32添加到64位native int时,CLR是否对32位整数进行符号扩展或零扩展?最重要的是:根据它做出选择的信息?


我正在编写.NET编译器,并且已经完全阅读了ECMA规范,但找不到答案。

  

CLI在其操作中仅对存储在其评估堆栈中的值支持这些类型的子集:int32int64native int。   
- ECMA 335,第I部分12.1:支持的数据类型

由于评估堆栈上的值没有关于其签名的信息,因此操作数签名的指令有两个变体:一个用于签名,一个用于无符号整数。只要操作数不同,addsubmul指令(不检查溢出的指令)就不需要关心操作数的签名大小相同,因此只有一个变体。但是,操作数并不总是相同的大小...

ECMA 335,第III节1.5:操作数类型表表明int32native 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

那么结果会是什么?请注意,根据规范,运行时不需要跟踪堆栈上值的确切类型或签名:它只知道int32int64native int(和其他一些与此无关的其他内容。)


我认为IntPtrUIntPtr算术,因为它在内部表示为本机int,也会使用这种加法。但是,ILSpy表明在C#中添加IntPtrInt32会调用IntPtr类上的重载+运算符,该类仅接受带符号的Int32参数。

直接在CIL中执行(使用add指令)也表示整数被解释为已签名。它也应该在Mono中实现,但我找不到任何提及我的发现的参考。

1 个答案:

答案 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语言设计中的错误或疏忽的结果。