Vector128.Create is not inserting the correct elements into the vector in some cases on .NET 5. The issue can be reproduced with the code below:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace VectorCreateBug
{
static class BitfieldExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ExtractSx(this long value, int lsb, int length)
{
int shift = lsb & 0x3f;
return (int)((value << (64 - (shift + length))) >> (64 - length));
}
}
class Program
{
private struct PackedValues
{
private long _e0;
public int Value0 => _e0.ExtractSx(0, 4);
public int Value1 => _e0.ExtractSx(4, 4);
public int Value2 => _e0.ExtractSx(8, 4);
public int Value3 => _e0.ExtractSx(12, 4);
public PackedValues(long packed)
{
_e0 = packed;
}
}
static void Main(string[] args)
{
// Run a few times to trigger the bug due to tiered compilation...
for (int i = 0; i < 1000; i++)
{
Test();
}
}
static void Test()
{
PackedValues p = new PackedValues(0x4321);
Vector128<int> test = Vector128.Create(p.Value0, p.Value1, p.Value2, p.Value3);
Console.WriteLine((test.GetElement(0) + test.GetElement(1) + test.GetElement(2) + test.GetElement(3)).ToString());
}
}
}
It should always print 10 since 1 + 2 + 3 + 4 = 10, however when running in release mode on .NET 5, it starts printing 9 after a few iterations. This issue only happens in release mode, and only on .NET 5, it works properly on .NET Core 3.1 (I did not try older versions).
Tested on .NET 5.0.100, OS Windows 10 x64, 9th generation Core i7 x64 CPU. I don't know if it's specific to this configuration as I didn't try on any other OS or machine yet.
It worked properly on .NET Core 3.1, I did not try versions older than that.
One possible workaround is disabling optimizations for the method using the [MethodImpl(MethodImplOptions.NoOptimization)] attribute, but that has performance impact so it's not ideal.
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
It's the same bug as https://github.com/dotnet/runtime/issues/43569 (was fixed recently in master via https://github.com/dotnet/runtime/pull/43578). A candidate for the service release?
Workaround for .NET5:
Vector128<int> test = Vector128.Create(p.Value0,p.Value1,p.Value2,p.Value3);
// don't use calls as arguments for Vector_.Create directly and introduce temp variables instead:
var v0 = p.Value0;
var v1 = p.Value1;
var v2 = p.Value2;
var v3 = p.Value3;
Vector128<int> test = Vector128.Create(v0,v1,v2,v3);
Workaround for .NET5:
Vector128<int> test = Vector128.Create(p.Value0,p.Value1,p.Value2,p.Value3); // don't use calls as arguments for Vector_.Create directly and introduce temp variables instead: var v0 = p.Value0; var v1 = p.Value1; var v2 = p.Value2; var v3 = p.Value3; Vector128<int> test = Vector128.Create(v0,v1,v2,v3);
That is a better looking workaround that what I did, might give it a shot :+1:
@Thog Please let us know how the workaround goes.
@EgorBo you're sure this is a dup of #43569?
@EgorBo you're sure this is a dup of #43569?
Yes, the workaround I posted in https://github.com/dotnet/runtime/issues/44704#issuecomment-727606796 works for the original repro https://github.com/dotnet/runtime/issues/44704#issue-743288114
Vector128<int> test = Vector128.Create(p.Value0, p.Value1, p.Value2, p.Value3);
these ValueX are propertes == methods. Also, the issue doesn't reproduce on net6.
@Thog Please let us know how the workaround goes.
I ended up not using the solution proposed by @EgorBo and kept the original workaround @gdkchan made when hitting the issue:
https://github.com/Ryujinx/Ryujinx/commit/aa129fdbdfd424f73da2b187fc28855f8ae90059#diff-9a4c528fa9a8d10cb90c5d5cb0004e77d54bc62bbf3b17a91f99b75d02901aa2R59
Closing as dup of #43569.
Fix will be in 5.0.1.
Most helpful comment
Workaround for .NET5: