潜在的 .NET x86 JIT 问题?
以下代码在发布模式(或启用优化的调试)下构建并在不附加 Visual Studio 调试器的情况下运行时,其行为有所不同。
它似乎也只有在使用 x86 JITter 时才会复制。我已经在 x86 机器上测试了它,并在 x64 机器上在 WOW64 中运行(通过将平台目标设置为 x86)。
我只在.NET 4.0 上尝试过这个。
当在 Release 中的调试器外部运行时,我看到:
Value is 4
当在调试器内部运行时,WriteLine
调用的 e.Value.Length
部分抛出 NullReferenceException
,这正是我所期望发生的事情。
代码:
namespace Test
{
class UsingReleasable<T>
{
public UsingReleasable(T obj)
{
m_obj = obj;
}
public T Release()
{
T tmp = m_obj;
m_obj = default(T);
return tmp;
}
public T Value
{
get { return m_obj; }
}
T m_obj;
}
class Program
{
static void Main(string[] args)
{
var e = new UsingReleasable<string>("test");
e.Release();
System.Console.WriteLine("Value is {0}", e.Value.Length);
}
}
}
我查看 JIT 生成的代码让我认为这是该部分中的一个错误,但我想在将其转发到 MS Connect 之前仔细检查一下。
The following code behaves differently when built in Release mode (or Debug with optimizations on) and run without the Visual Studio debugger attached.
It also only seems to replicate if the x86 JITter is used. I have tested this on an x86 machine as well as running in WOW64 on a x64 machine (by setting the Platform target to x86).
I've only tried this with .NET 4.0.
When running outside the debugger in Release I see:
Value is 4
When running inside the debugger the e.Value.Length
portion of the WriteLine
call throws NullReferenceException
, which is what I expected to have happen.
The code:
namespace Test
{
class UsingReleasable<T>
{
public UsingReleasable(T obj)
{
m_obj = obj;
}
public T Release()
{
T tmp = m_obj;
m_obj = default(T);
return tmp;
}
public T Value
{
get { return m_obj; }
}
T m_obj;
}
class Program
{
static void Main(string[] args)
{
var e = new UsingReleasable<string>("test");
e.Release();
System.Console.WriteLine("Value is {0}", e.Value.Length);
}
}
}
My peering at the JIT generated code makes me think it is a bug in that piece, but I wanted to double check here before forwarding this on to MS Connect.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我可以重现你的行为:
/checked
编译器选项没有区别。调试数据/debug+
的生成也不会。当调用Console.ReadLine()
时,问题仍然存在(以便有机会附加调试器并查看优化的代码。我对
Main
进行了轻微修改以允许调试优化代码的:以及实际的反汇编:
当程序在调试器下启动时,会生成此代码(并且确实产生
NullReferenceException
:我想我已经注释了所有相关的代码行显然是错误的版本。寄存器
edi
用于保存指向e.Value
的指针,它由很可能是指向内容的指针(在偏移量0处)和一个长度(在偏移量处)组成。 4) v-table 的(在偏移量 0 处)、长度(在偏移量 4 处),不幸的是,后面紧跟的是内容e.Value
(字符串“test”)。
)被复制到edi
在e.Value
被清除之前,因此使用错误的字符串指针获取长度。哎呀!在 Connect 上提交的错误(请投票!):x86 JIT 不正确地重新排序泛型类型字段的加载并分配给默认值(T)
I can reproduce your behavior:
The
/checked
compiler option makes no difference. Neither does generation of debug data/debug+
. And the problem persists whenConsole.ReadLine()
is called (to give a chance to attach the debugger and see optimized code.I made a slight modification to
Main
to permit debugging of the optimized code:And the actual disassembly:
When the program starts under the debugger, this code is generated instead (and does produce a
NullReferenceException
:I think I've commented all the relevant lines of code in the incorrect version. Clearly register
edi
is used to hold a pointer toe.Value
, which consistsmost likely of a pointer to content (at offset 0) and a length (at offset 4)of the v-table (at offset 0), length (at offset 4), followed immediately by the content. Unfortunatelye.Value
(the string"test"
) is copied intoedi
beforee.Value
is cleared, so the length is fetched using the wrong string pointer. Ouch!Bug filed on Connect (please upvote!): x86 JIT improperly reorders load of field of generic type with assignment to default(T)