为什么多维数组的枚举值不等于自身?
Why is the enumeration value from a multi dimensional array not equal to itself?
考虑:
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
也重现。)
让我们考虑一下 OP 的声明:
enum State : sbyte { OK = 0, BUG = -1 }
因为错误只发生在 BUG
为负(从 -128 到 -1)并且 State 是 signed byte 的枚举时,我开始假设是某个地方的演员表问题。
如果你运行这个:
Console.WriteLine((sbyte)s[0, 0]);
Console.WriteLine((sbyte)State.BUG);
Console.WriteLine(s[0, 0]);
unchecked
{
Console.WriteLine((byte) State.BUG);
}
它将输出:
255
-1
BUG
255
出于我忽略的原因(截至目前) s[0, 0]
在评估之前被转换为一个字节,这就是为什么它声称 a == s[0,0]
是假的。
您在 .NET 4 x86 抖动中发现了一个代码生成错误。这是一个非常不寻常的问题,只有在代码未优化时才会失败。机器代码如下所示:
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 位整数的必要转换发生的地方。数组 getter 辅助函数返回 0x0000000FF,等于 State.BUG,在比较值之前需要将其转换为 -1 (0xFFFFFFFF)。 MOVSX 指令是 Sign eXtension 指令。
同样的事情在 013F04CC 再次发生,但这次有没有 MOVSX 指令进行相同的转换。那就是芯片掉下来的地方,CMP 指令将 0xFFFFFFFF 与 0x000000FF 进行比较,这是错误的。所以这是一个遗漏错误,代码生成器未能再次发出 MOVSX 以执行相同的 sbyte 到 int 转换。
这个错误特别不寻常的是,当您启用优化器时它可以正常工作,它现在知道在两种情况下都使用 MOVSX。
此错误长时间未被发现的可能原因是使用 sbyte 作为枚举的基本类型。很少有人做。使用多维数组也有帮助,组合是致命的。
否则我会说这是一个非常严重的错误。很难猜测它的传播范围有多广,我只有 4.6.1 x86 抖动要测试。 x64 和 3.5 x86 抖动生成非常不同的代码并避免此错误。继续前进的临时解决方法是删除 sbyte 作为枚举基类型并让它成为默认值,int,因此不需要符号扩展。
您可以在 connect.microsoft.com 提交错误,链接到这个 Q+A 应该足以告诉他们他们需要知道的一切。如果您不想花时间,请告诉我,我会处理的。
考虑:
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
也重现。)
让我们考虑一下 OP 的声明:
enum State : sbyte { OK = 0, BUG = -1 }
因为错误只发生在 BUG
为负(从 -128 到 -1)并且 State 是 signed byte 的枚举时,我开始假设是某个地方的演员表问题。
如果你运行这个:
Console.WriteLine((sbyte)s[0, 0]);
Console.WriteLine((sbyte)State.BUG);
Console.WriteLine(s[0, 0]);
unchecked
{
Console.WriteLine((byte) State.BUG);
}
它将输出:
255
-1
BUG
255
出于我忽略的原因(截至目前) s[0, 0]
在评估之前被转换为一个字节,这就是为什么它声称 a == s[0,0]
是假的。
您在 .NET 4 x86 抖动中发现了一个代码生成错误。这是一个非常不寻常的问题,只有在代码未优化时才会失败。机器代码如下所示:
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 位整数的必要转换发生的地方。数组 getter 辅助函数返回 0x0000000FF,等于 State.BUG,在比较值之前需要将其转换为 -1 (0xFFFFFFFF)。 MOVSX 指令是 Sign eXtension 指令。
同样的事情在 013F04CC 再次发生,但这次有没有 MOVSX 指令进行相同的转换。那就是芯片掉下来的地方,CMP 指令将 0xFFFFFFFF 与 0x000000FF 进行比较,这是错误的。所以这是一个遗漏错误,代码生成器未能再次发出 MOVSX 以执行相同的 sbyte 到 int 转换。
这个错误特别不寻常的是,当您启用优化器时它可以正常工作,它现在知道在两种情况下都使用 MOVSX。
此错误长时间未被发现的可能原因是使用 sbyte 作为枚举的基本类型。很少有人做。使用多维数组也有帮助,组合是致命的。
否则我会说这是一个非常严重的错误。很难猜测它的传播范围有多广,我只有 4.6.1 x86 抖动要测试。 x64 和 3.5 x86 抖动生成非常不同的代码并避免此错误。继续前进的临时解决方法是删除 sbyte 作为枚举基类型并让它成为默认值,int,因此不需要符号扩展。
您可以在 connect.microsoft.com 提交错误,链接到这个 Q+A 应该足以告诉他们他们需要知道的一切。如果您不想花时间,请告诉我,我会处理的。