Ошибка, только происходящая, когда оптимизация компиляции включена

Я столкнулся с ошибкой в коде, который только воспроизводится, когда код создается с включенной оптимизацией. Я сделал консольное приложение, которое копирует логику для тестирования (код ниже). Вы будете видеть, что, когда оптимизации включают, 'значение' становится пустым после выполнения этой недопустимой логики:

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 является VM рабочий Windows Server 2003 R2 с.NET 3,5 SP1. Использование Системы Команды VS2008.

Спасибо,

Brian

44
задан bmancini 26 January 2010 в 16:46
поделиться

5 ответов

Да, ваше выражение смертельно пушает оптимизатора 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 для String.concat ().

Для сравнения, это код, который генерируется, когда jit Optimization отключается:

                    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);
  }
45
ответ дан 26 November 2019 в 22:18
поделиться

Я нахожусь на X64, и сначала не мог воспроизвести проблему. Затем я указал цель как x86, и это случилось со мной. Вернуться к X64, и ушло. Не уверен, что именно это значит, но теперь я вернулся взад и вперед несколько раз.

1
ответ дан 26 November 2019 в 22:18
поделиться
value == new string[0]

Для меня это выглядит как странное заявление. Вы сравниваете два массива строк с утверждением равным. Это приведет к истине только в том случае, если они оба будут указывать на один и тот же массив, что маловероятно. Пока это не объясняет, почему в оптимизированном варианте код ведет себя по-другому.

2
ответ дан 26 November 2019 в 22:18
поделиться

Конечно, похоже на ошибку, она воспроизводится, когда вы меняете операторы, как этот?

if (false == (null == value || new string[0] == value))
0
ответ дан 26 November 2019 в 22:18
поделиться

Эта ошибка, похоже, была исправлена ​​в .NET 4 ( бета 2). Вот оптимизированная разборка x86 для выделенного бита nobugz выше:

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

Программа также отображает ожидаемый результат как в оптимизированном, так и в неоптимизированном режимах.

3
ответ дан 26 November 2019 в 22:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: