仅在启用编译优化时发生错误

时间:2010-01-25 20:47:37

标签: c# compiler-optimization

我遇到了代码中的错误,只有在启用了优化的情况下构建代码时才会重现。我制作了一个控制台应用程序来复制测试逻辑(下面的代码)。你会看到,当启用优化时,'value'在执行这个无效逻辑后变为null:

if ((value == null || value == new string[0]) == false)

修复是直截了当的,并在违规代码下方注释掉。但是......我更担心的是我可能遇到了汇编程序中的错误,或者其他人可能会解释为什么将值设置为null。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace memory_testing
{
    class Program
    {
        sta tic void Main(string[] args)
        {
            while(true)
            {
                Console.Write("Press any key to start...");
                Console.ReadKey();
                Console.WriteLine();
                PrintManagerUser c = new PrintManagerUser();
                c.MyProperty = new string[1];
            }
        }
    }

    public class PrintManager
    {
        public void Print(string key, object value)
        {
            Console.WriteLine("Key is: " + key);
            Console.WriteLine("Value is: " + value);
        }
    }

    public class PrintManagerUser
    {
        public string[] MyProperty
        {
            get { return new string[100]; }
            set
            {
                Console.WriteLine("Pre-check Value is: " + value);
                if ((value == null || value == new string[0]) == false)
                {
                    Console.WriteLine("Post-check Value is: " + value);
                    new PrintManager().Print("blah", value);
                }
                //if (value != null && value.Length > 0)
                //{
                //    new PrintManager().Print("blah", value);
                //}
            }
        }
    }
}

正常输出应为:

Pre-check Value is: System.String[]
Post-check Value is: System.String[]
Key is: blah
Value is: System.String[]

错误的输出是:

Pre-check Value is: System.String[]
Post-check Value is:
Key is: blah
Value is:   

我的Env是运行带有.NET 3.5 SP1的Windows Server 2003 R2的VM。使用VS2008 Team System。

谢谢,

布赖恩

5 个答案:

答案 0 :(得分:45)

是的,你的表达式致命地混淆了JIT优化器。生成的机器代码如下所示:

                if ((value == null || value == new string[0]) == false)
00000027  test        esi,esi               ; value == null?
00000029  je          00000075 
0000002b  xor         edx,edx               ; new string[0]
0000002d  mov         ecx,6D913BD2h 
00000032  call        FFD20BC8 
00000037  cmp         eax,esi               ; (value == new string[0]) == false?
00000039  je          00000075 
                {
                    Console.WriteLine("Post-check Value is: " + value);
0000003b  mov         ecx,dword ptr ds:[03532090h]  ; "Post-check value is: "
00000041  xor         edx,edx               ; BUGBUG not null!
00000043  call        6D70B7E8              ; String.Concat()
00000048  mov         esi,eax               ; 
0000004a  call        6D72BE08              ; get Console.Out
0000004f  mov         ecx,eax 
00000051  mov         edx,esi 
00000053  mov         eax,dword ptr [ecx] 
00000055  call        dword ptr [eax+000000D8h]     ; Console.WriteLine()

错误发生在地址41,优化器得出结论,值始终为null,因此它直接将null传递给String.Concat()。

为了进行比较,这是关闭JIT优化时生成的代码:

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[03342090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        6D77B790 

代码被移动了,但请注意,在地址5c,它现在使用局部变量(值)而不是null。

您可以在connect.microsoft.com上报告此错误。解决方法很简单:

  if (value != null)
  {
    Console.WriteLine("Post-check Value is: " + value);
    new PrintManager().Print("blah", value);
  }

答案 1 :(得分:3)

这个错误似乎已在.NET 4(beta 2)中得到修复。这是上面突出显示的位nobugz的优化x86反汇编:

                    Console.WriteLine("Post-check Value is: " + value);
00000056  mov         ecx,dword ptr ds:[033C2090h] 
0000005c  mov         edx,dword ptr [ebp-8] 
0000005f  call        65D8FE10

该程序还以优化和未优化模式显示预期输出。

答案 2 :(得分:2)

value == new string[0]

以上看起来像是一个奇怪的声明。您正在将两个字符串数组与equals语句进行比较。如果它们都指向同一个数组,那只会导致为真,这是不太可能的。这并没有解释为什么这个代码在优化版本中表现不同。

答案 3 :(得分:1)

我在x64上并且最初无法重现该问题。然后我将目标指定为x86,它发生在我身上。回到x64,它就消失了。不确定这究竟是什么意思,但我现在已经来过几次了。

答案 4 :(得分:0)

当然看起来像一个错误,当你交换像这样的操作员操作数时它会重现吗?

if (false == (null == value || new string[0] == value))