RyuJit产生了错误的结果。

35

我们最近升级到.net 4.6后发现一个错误,RyuJit生成了不正确的结果。我们目前通过在app.config中添加useLegacyJit enabled="true"来解决此问题。

如何调试以下代码生成的机器码?

我在VS 2015 RTM中创建了一个新的控制台项目,设置为Release、Any CPU,取消选中Prefer 32位,在运行时使用/不使用附加的调试器产生相同的结果。

using System;
using System.Runtime.CompilerServices;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Calculate());
            Console.WriteLine(Calculate());

            Console.ReadLine();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Value Calculate()
        {
            bool? _0 = (bool?)null;
            bool? _1 = (bool?)true;
            if (!Value.IsPresent<bool>(_1))
            {
                return default(Value);
            }

            bool? result = null;
            result = (_1.Value ? new bool?(false) : result);
            if (_0.HasValue && _0.Value)
            {
            }
            return new Value(result);
        }

        public struct Value
        {
            bool? _value;

            public Value(bool? value)
            {
                _value = value;
            }

            public static bool IsPresent<T>(bool? _)
            {
                return _.HasValue;
            }

            public override string ToString()
            {
                return _value.ToString();
            }
        }
    }
}

它应该产生: False False

但实际上它产生了: True False

这个例子的关键部分是

result = true ? false : result;

这个方法应该总是返回false,但是从输出可以看出,第一次运行时它却返回了True,第二次运行则返回了不同的答案。从Calculate()方法中删除更多的行将导致它总是返回True,但是给定的示例是我能够重现我们实际生产场景的最接近的情况。


5
是的,这是一种内联优化错误。第一个内联的Calculate()函数有一条错误指令。在我的看法中,优化器中有一个不应该存在的!符号。当然我们无法修复这个错误,你可以在connect.microsoft.com上报告它。暂时停用MethodImplOptions.AggressiveInlining,在编写代码时避免过度使用,因为这可能没有经过充分测试。 - Hans Passant
实际的生产代码没有AggressiveInlining属性,但这是我在这个小例子中重现行为的唯一方法。生产代码部分是机器生成的,这就是为什么示例代码看起来有点奇怪。我刚刚在https://connect.microsoft.com/VisualStudio/feedback/details/1578173上报告了它。 - BrandonAGr
@BrandonAGr 这个 bug 是由于内联引起的吗?使用 MethodImplOptions.NoInlining 是一个可能的解决方法吗? - Ortiga
2
我已经向coreclr仓库报告了这个bug:https://github.com/dotnet/coreclr/issues/1299 在这个问题中,您可以找到一个带有汇编清单的简化版本的代码。 - AndreyAkinshin
似乎与http://stackoverflow.com/questions/17483585/recursion-in-windows-7-64-bit相同。 - Bruno Canettieri
1个回答

27

感谢提供这个隔离重现程序,我确认这确实是RyuJIT优化器中的一个bug,由于内联而暴露出来。我已经修复了编译器,并正在考虑推出细节。为了不把SO变成一个错误跟踪器,并为了更快的处理结果:请联系ryujit@microsoft.com。


3
我已经建立了源代码的简化版本,你可以在 coreclr 仓库中找到它:https://github.com/dotnet/coreclr/issues/1299 - AndreyAkinshin
1
@HansPassant,是的,我并不是想把这个当作一个错误报告,而是想知道如何找到正在执行的机器代码。Andrey提供的简化示例使得在Visual Studio中查看反汇编变得非常容易,当然,仍然很难意识到哪些寄存器保存了什么内容,但我能够逐步进行。 - BrandonAGr
@schellap 这个修复程序是否也修复了 - https://connect.microsoft.com/VisualStudio/Feedback/Details/1602437 ? - Stephen Gennard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接