Почему значение перечисления из многомерного массива не равно самому себе?
Рассмотрим:
using System;
public class Test
{
enum State : sbyte { OK = 0, BUG = -1 }
static void Main(string[] args)
{
var s = new State[1, 1];
s[0, 0] = State.BUG;
State a = s[0, 0];
Console.WriteLine(a == s[0, 0]); // False
}
}
Как это можно объяснить? Это происходит в отладочных сборках в Visual Studio 2015 при запуске в x86 JIT. Сборка выпуска или запуск в x64 JIT печатает True, как и ожидалось.
Для воспроизведения из командной строки:
csc Test.cs /platform:x86 /debug
(/debug:pdbonly
, /debug:portable
и /debug:full
также размножаются.)
2 ответа:
Вы нашли ошибку генерации кода в .NET 4 x86 jitter. Это очень необычный метод, он дает сбой только тогда, когда код не оптимизирован. Машинный код выглядит так:
State a = s[0, 0]; 013F04A9 push 0 ; index 2 = 0 013F04AB mov ecx,dword ptr [ebp-40h] ; s[] reference 013F04AE xor edx,edx ; index 1 = 0 013F04B0 call 013F0058 ; eax = s[0, 0] 013F04B5 mov dword ptr [ebp-4Ch],eax ; $temp1 = eax 013F04B8 movsx eax,byte ptr [ebp-4Ch] ; convert sbyte to int 013F04BC mov dword ptr [ebp-44h],eax ; a = s[0, 0] Console.WriteLine(a == s[0, 0]); // False 013F04BF mov eax,dword ptr [ebp-44h] ; a 013F04C2 mov dword ptr [ebp-50h],eax ; $temp2 = a 013F04C5 push 0 ; index 2 = 0 013F04C7 mov ecx,dword ptr [ebp-40h] ; s[] reference 013F04CA xor edx,edx ; index 1 = 0 013F04CC call 013F0058 ; eax = s[0, 0] 013F04D1 mov dword ptr [ebp-54h],eax ; $temp3 = eax ; <=== Bug here! 013F04D4 mov eax,dword ptr [ebp-50h] ; a == s[0, 0] 013F04D7 cmp eax,dword ptr [ebp-54h] 013F04DA sete cl 013F04DD movzx ecx,cl 013F04E0 call 731C28F4
Трудное дело с большим количеством временных интервалов и дублированием кода, это нормально для неоптимизированного кода. Примечательна инструкция на 013F04B8, где происходит необходимое преобразование из sbyte в 32-разрядное целое число. Массив вспомогательную функцию добытчика вернулся 0x0000000FF, равна государства.Жучок, и все такое необходимо преобразовать в -1 (0xffffffffff), прежде чем значение можно будет сравнить. Инструкция MOVSX-это инструкция расширения знака.
То же самое происходит снова в 013F04CC, но на этот раз нет Инструкции MOVSX для выполнения того же преобразования. Вот где фишки падают, инструкция CMP сравнивает 0xFFFFFFFF с 0x000000FF, и это ложь. Так что это ошибка пропускания, генератор кода не смог снова выдать MOVSX для выполнения того же sbyte к int преобразование.
Что особенно необычно в этой ошибке, это то, что она работает правильно, когда вы включаете оптимизатор, теперь он знает, чтобы использовать MOVSX в обоих случаях.
Вероятной причиной того, что эта ошибка оставалась незамеченной так долго, является использование sbyte в качестве базового типа перечисления. Довольно редкое занятие. Использование многомерного массива также играет важную роль, комбинация фатальна.В противном случае довольно критическая ошибка, Я бы сказал. Насколько широко это может быть распространено, трудно догадаться, я есть только 4.6.1 x86 джиттер для тестирования. X64 и 3.5 x86 jitter генерируют очень разный код и избегают этой ошибки. В качестве временного решения проблемы, чтобы продолжать идти, чтобы удалить тип sbyte в качестве базового перечислимого типа и пусть это будет по умолчанию, инт, так нет знаковое расширение необходимо.
Вы можете подать сообщение об ошибке по адресу connect.microsoft.com, привязки к этому Q+A должно быть достаточно, чтобы рассказать им все, что им нужно знать. Дайте мне знать, если вы не хотите тратить время, и я позабочусь об этом.
Рассмотрим заявление ОП:
Так как ошибка возникает только тогда, когдаenum State : sbyte { OK = 0, BUG = -1 }
BUG
отрицательно (от -128 до -1) и состояние является перечислением подписанного байта, я начал предполагать, что где-то есть проблема приведения.Если вы выполните это:
Console.WriteLine((sbyte)s[0, 0]); Console.WriteLine((sbyte)State.BUG); Console.WriteLine(s[0, 0]); unchecked { Console.WriteLine((byte) State.BUG); }
Он выведет :
255
-1
Ошибка
255
По причине, которую я игнорирую (на данный момент)
s[0, 0]
приводится к байту перед вычислением, и вот почему он утверждает, чтоa == s[0,0]
является ложным.