C#是'types检查结构奇怪的.NET 4.0 x86优化行为

更新:我已经提交了Microsoft Connect的错误报告 ,请投票!

更新2:微软已经将bug报告标记为已修复

由微软于18/08/2010在17:25发布

该错误将在未来版本的运行时中修复。 恐怕现在判断是否会在服务包或下一个主要版本中还为时过早。

由于升级到VS2010,我得到一些非常奇怪的行为与“is”关键字。

下面的程序(test.cs)在debugging模式(对于x86)编译时输出True,在(对于x86)优化编译时输出False。 编译x64或AnyCPU中的所有组合会给出预期结果,即True。

在.NET 3.5下编译的所有组合都会给出预期的结果,True。

我正在使用下面的batch file(runtest.bat)来编译和testing使用编译器.NET框架的各种组合的代码。

  • 有没有人在.NET 4.0下看到过这种types的问题?
  • 当运行runtests.bat时,其他人是否在计算机上看到和我一样的行为?
  • @ $ @#$?

  • 有没有解决这个问题?

test.cs中

using System; public class Program { public static bool IsGuid(object item) { return item is Guid; } public static void Main() { Console.Write(IsGuid(Guid.NewGuid())); } } 

runtest.bat

 @echo off rem Usage: rem runtest -- runs with csc.exe x86 .NET 4.0 rem runtest 64 -- runs with csc.exe x64 .NET 4.0 rem runtest v3.5 -- runs with csc.exe x86 .NET 3.5 rem runtest v3.5 64 -- runs with csc.exe x64 .NET 3.5 set version=v4.0.30319 set platform=Framework for %%a in (%*) do ( if "%%a" == "64" (set platform=Framework64) if "%%a" == "v3.5" (set version=v3.5) ) echo Compiler: %platform%\%version%\csc.exe set csc="C:\Windows\Microsoft.NET\%platform%\%version%\csc.exe" set make=%csc% /nologo /nowarn:1607 test.cs rem CS1607: Referenced assembly targets a different processor rem This happens if you compile for x64 using csc32, or x86 using csc64 %make% /platform:x86 test.exe echo =^> x86 %make% /platform:x86 /optimize test.exe echo =^> x86 (Optimized) %make% /platform:x86 /debug test.exe echo =^> x86 (Debug) %make% /platform:x86 /debug /optimize test.exe echo =^> x86 (Debug + Optimized) %make% /platform:x64 test.exe echo =^> x64 %make% /platform:x64 /optimize test.exe echo =^> x64 (Optimized) %make% /platform:x64 /debug test.exe echo =^> x64 (Debug) %make% /platform:x64 /debug /optimize test.exe echo =^> x64 (Debug + Optimized) %make% /platform:AnyCPU test.exe echo =^> AnyCPU %make% /platform:AnyCPU /optimize test.exe echo =^> AnyCPU (Optimized) %make% /platform:AnyCPU /debug test.exe echo =^> AnyCPU (Debug) %make% /platform:AnyCPU /debug /optimize test.exe echo =^> AnyCPU (Debug + Optimized) 

检测结果

运行runtest.bat时,我在Win7 x64安装中得到以下结果。

 > runtest 32 v4.0 Compiler: Framework\v4.0.30319\csc.exe False => x86 False => x86 (Optimized) True => x86 (Debug) False => x86 (Debug + Optimized) True => x64 True => x64 (Optimized) True => x64 (Debug) True => x64 (Debug + Optimized) True => AnyCPU True => AnyCPU (Optimized) True => AnyCPU (Debug) True => AnyCPU (Debug + Optimized) > runtest 64 v4.0 Compiler: Framework64\v4.0.30319\csc.exe False => x86 False => x86 (Optimized) True => x86 (Debug) False => x86 (Debug + Optimized) True => x64 True => x64 (Optimized) True => x64 (Debug) True => x64 (Debug + Optimized) True => AnyCPU True => AnyCPU (Optimized) True => AnyCPU (Debug) True => AnyCPU (Debug + Optimized) > runtest 32 v3.5 Compiler: Framework\v3.5\csc.exe True => x86 True => x86 (Optimized) True => x86 (Debug) True => x86 (Debug + Optimized) True => x64 True => x64 (Optimized) True => x64 (Debug) True => x64 (Debug + Optimized) True => AnyCPU True => AnyCPU (Optimized) True => AnyCPU (Debug) True => AnyCPU (Debug + Optimized) > runtest 64 v3.5 Compiler: Framework64\v3.5\csc.exe True => x86 True => x86 (Optimized) True => x86 (Debug) True => x86 (Debug + Optimized) True => x64 True => x64 (Optimized) True => x64 (Debug) True => x64 (Debug + Optimized) True => AnyCPU True => AnyCPU (Optimized) True => AnyCPU (Debug) True => AnyCPU (Debug + Optimized) 

TL;博士

我做了一个类似的例子,

 using System; using System.Runtime.CompilerServices; public class Program { static void Main() { Console.Write(Verify(Test.Create())); Console.ReadLine(); } //[MethodImpl(MethodImplOptions.NoInlining)] static bool Verify(IDisposable item) { return item is Test; } struct Test : IDisposable { public void Dispose() { } public static Test Create() { return new Test(); } } } 

这是一个JIT优化器错误。 不能完全放在手指上,它会严重地优化代码。 但是在我看来,当它优化拳击转换时,它会遇到麻烦。 非常严重的错误,坦率地说。


这个bug已经修复了,我再也不能再重复它了。 我当前版本的clrjit.dll是4.0.30319.237date为2011年5月17日。我无法确切地知道什么更新修复它。 2011年8月5日我收到了一个安全更新,更新clrjit.dll到版本235,date为4月12日,这将是最早的。

要回答最后一个问题,您可以将MethodImpl属性的MethodImpl选项添加到您的IsGuid方法中,作为解决此问题的解决方法。

我只是做了一个简单的testing,在.NET 4.0上的x86debugging和发布configuration之间切换,这似乎解决了这个问题。 我还没有运行你的runtests.bat。

如果还没有提交,您还应该在Connect中提交问题,并将其与您的问题链接起来。

这不是很酷。 我认为你应该提交一个错误。 (connect.microsoft.com)

此外,这似乎工作(我只testing你的失败案例虽然):

  public static bool IsGuid(object item) { return item.GetType() == typeof(Guid); } 

除了几个小数点以外,reflection器指示的唯一区别在于IsGuid方法:

DEBUG:

 .method public hidebysig static bool IsGuid(object item) cil managed { .maxstack 2 .locals init ( [0] bool CS$1$0000) L_0000: nop L_0001: ldarg.0 L_0002: isinst [mscorlib]System.Guid L_0007: ldnull L_0008: cgt.un L_000a: stloc.0 L_000b: br.s L_000d L_000d: ldloc.0 L_000e: ret } 

发布:

 .method public hidebysig static bool IsGuid(object item) cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: isinst [mscorlib]System.Guid L_0006: ldnull L_0007: cgt.un L_0009: ret } 

我不够stream利解释这些差异,但这是一个社区wiki的答案,所以也许别人可以启发我们?

改变方法,使其通用作品围绕(可能的错误?)

 public static bool IsGuid<T>(T item) { return item is Guid; } 

正如强制它成为一个本地variables(但它必须在方法中使用,以防止优化踢):

 public static bool IsGuid(object item) { bool a = item is Guid; a.ToString(); return a; } 

这是我在XP SP3(x86)上的结果:

 >runtest Compiler: Framework\v4.0.30319\csc.exe False => x86 False => x86 (Optimized) True => x86 (Debug) False => x86 (Debug + Optimized) False => AnyCPU False => AnyCPU (Optimized) True => AnyCPU (Debug) False => AnyCPU (Debug + Optimized) >runtest v3.5 Compiler: Framework\v3.5\csc.exe True => x86 True => x86 (Optimized) True => x86 (Debug) True => x86 (Debug + Optimized) True => AnyCPU True => AnyCPU (Optimized) True => AnyCPU (Debug) True => AnyCPU (Debug + Optimized) 

关于这个结果有趣的一点是,.NET 4目标在AnyCPU上运行(作为x86运行),但在AnyCPU上运行(以x64运行)。

有趣的是,这个工作正确:

 using System; public class Program { public static bool IsGuid(object item) { return item is Guid; } public static void Main() { Guid s = Guid.NewGuid(); Console.Write(IsGuid(s)); } } 

这里是在不同的il:

 .method public hidebysig static void Main() cil managed { .entrypoint // Code size 23 (0x17) .maxstack 1 .locals init (valuetype [mscorlib]System.Guid V_0) IL_0000: call valuetype [mscorlib]System.Guid [mscorlib]System.Guid::NewGuid() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: box [mscorlib]System.Guid IL_000c: call bool Program::IsGuid(object) IL_0011: call void [mscorlib]System.Console::Write(bool) IL_0016: ret } // end of method Program::Main