LocoDelAssembly LocoDelAssembly - 11 months ago 71
Vb.net Question

CInt(Long) in VB.NET behaving differently in 32- and 64-bit environments

Today I had a problem converting a Long (Int64) to an Integer (Int32). The problem is that my code was always working in 32-bit environments, but when I try THE SAME executable in a 64-bit computer it crashes with a System.OverflowException exception.

I've prepared this test code in Visual Studio 2008 in a new project with default settings:

Module Module1

Sub Main()
Dim alpha As Long = -1
Dim delta As Integer

delta = CInt(alpha And UInteger.MaxValue)
Console.WriteLine("CINT OK")
delta = Convert.ToInt32(alpha And UInteger.MaxValue)
Console.WriteLine("Convert.ToInt32 OK")
Catch ex As Exception
End Try
End Sub

End Module

On my 32-bit setups (Windows XP SP3 32-bit and Windows 7 32-bit) it prints up to "CINT OK", but in the 64-bit computer (Windows 7 64-bit) that I've tested THE SAME executable it prints the exception name only.

Is this behavior documented? I tried to find a reference, but I failed miserably.

For reference I leave the CIL code too:

.method public static void Main() cil managed
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// Code size 88 (0x58)
.maxstack 2
.locals init ([0] int64 alpha,
[1] int32 delta,
[2] class [mscorlib]System.Exception ex)
IL_0000: nop
IL_0001: ldc.i4.m1
IL_0002: conv.i8
IL_0003: stloc.0
IL_0004: nop
IL_0005: ldloc.0
IL_0006: ldc.i4.m1
IL_0007: conv.u8
IL_0008: and
IL_0009: conv.ovf.i4
IL_000a: stloc.1
IL_000b: ldstr "CINT OK"
IL_0010: call void [mscorlib]System.Console::WriteLine(string)
IL_0015: nop
IL_0016: ldloc.0
IL_0017: ldc.i4.m1
IL_0018: conv.u8
IL_0019: and
IL_001a: call int32 [mscorlib]System.Convert::ToInt32(int64)
IL_001f: stloc.1
IL_0020: ldstr "Convert.ToInt32 OK"
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: nop
IL_002b: leave.s IL_0055
} // End .try
catch [mscorlib]System.Exception
IL_002d: dup
IL_002e: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception)
IL_0033: stloc.2
IL_0034: nop
IL_0035: ldloc.2
IL_0036: callvirt instance class [mscorlib]System.Type [mscorlib]System.Exception::GetType()
IL_003b: callvirt instance string [mscorlib]System.Type::ToString()
IL_0040: call void [mscorlib]System.Console::WriteLine(string)
IL_0045: nop
IL_0046: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
IL_004b: leave.s IL_0055
} // End handler
} // End .try
IL_004d: nop
IL_004e: call string [mscorlib]System.Console::ReadLine()
IL_0053: pop
IL_0054: endfinally
} // End handler
IL_0055: nop
IL_0056: nop
IL_0057: ret
} // End of method Module1::Main

I suspect that the instruction that is behaving differently is either conv.ovf.i4 or the ldc.i4.m1/conv.u8 pair.

What is going on?

fails in both environments. It is only CInt(Long) which is behaving differently.

Answer Source

Unfortunately, the 64-bit version is accurate. It really is an overflow, the result of the expression is a long with the value &hffffffff. The sign bit is AND-ed off the value, it is no longer negative. The resulting value cannot be converted to an integer, the maximum integer value is &h7fffffff. You can see this by adding this code to your snippet:

 Dim value As Long = alpha And UInteger.MaxValue

Output: 4294967295

The x64 jitter uses an entirely different way to check for overflows, it doesn't rely on the CPU overflow exception but explicitly compares the values to Integer.MaxValue and Integer.MinValue. The x86 jitter gets it wrong, it optimizes the code too much and ends up making an unsigned operation that doesn't trip the CPU exception.

Filing a bug report at connect.microsoft.com is probably not worth the effort, fixing this for the x86 jitter would be a drastically breaking change. You'll have to rework this logic. Not sure how, I don't see what you are trying to do.