You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Assume following example (stripped down from real source code) which should load a struct with a kind of literal value.
In the Test() method, three variables x, y, z are initialized with 1.0, 2.0 and 1.0 again.
public struct MyStruct
{
public ulong A0, A1, A2, A3;
public ulong B0, B1, B2, B3;
}
public sealed class TestCase
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LoadInline(out MyStruct result, ulong value) {
result = default;
result.A0 = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LoadInline(out MyStruct result, double value) {
// Branch is JIT inline removable, when value is a compile time constant
if (value == 1.0) {
LoadInline(out result, 1);
return;
}
// Branch is JIT inline removable, when value is a compile time constant
if (value == 2.0) {
LoadInline(out result, 2);
return;
}
// Use default Value
LoadInline(out result, 0);
// Strange behavior, when code is active
// ulong ul = Unsafe.As<double, ulong>(ref value); // <-- LINE A
// result.A1 = ul; // <-- LINE B
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void SomeUsage(in MyStruct result) {
;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Test() {
LoadInline(out var x, 1.0);
LoadInline(out var y, 2.0);
LoadInline(out var z, 1.0);
SomeUsage(x);
SomeUsage(y);
SomeUsage(z);
}
}
Case 1: When you compile to a optimized release build following optimal code is generated for the three LoadInline() calls.
[SkipLocalsInit is NOT used, so at the begin of the Test() method local memory is already initialized]
LoadInline(out var x, 1.0);
00007FFB079BAA0B mov dword ptr [rsp+0A8h],1
LoadInline(out var y, 2.0);
00007FFB079BAA16 mov dword ptr [rsp+68h],2
LoadInline(out var z, 1.0);
00007FFB079BAA1E mov dword ptr [rsp+28h],1
Case 2: When LINE A and LINE B are both activated, following sub optimal (but working) code is produced.
The second and third call perform a redundant zero out of the struct. Interestingly the first call is still optimal. Observe the fact that the method arguments in call 1 and call 3 are the same, but produce different code.
Case 3: Even more strange - but not my real world use case - the optimizer gets totaly confused, when only LINE A is active and LINE B is commented out. The code is still working, but obviously the JIT constant branch removal has totaly failed.
All three variants should produce the same optimal code.
LoadInline(out var x, 1.0);
00007FFB079BAA0B mov dword ptr [rsp+0A8h],1
LoadInline(out var y, 2.0);
00007FFB079BAA16 mov dword ptr [rsp+68h],2
LoadInline(out var z, 1.0);
00007FFB079BAA1E mov dword ptr [rsp+28h],1
Actual Behavior:
Case 1: The optimal code :-)
Case 2: For large enough structs the code and runtime behavior is very bad and seems unsymetric between the same method calls.
Case 3: This is not a real use case because a senseless Unsafe cast is made without any usage. But no warning is raised, when you accidentally compile such a source code.
Off Topic:
It would be great - especially for my use case - when a method argument could be restricted to a compile time constant.
The text was updated successfully, but these errors were encountered:
Version Used: NET5
Not a hard bug - the suboptimal code is still logical correct.
Steps to Reproduce:
Compiler settings
Assume following example (stripped down from real source code) which should load a struct with a kind of literal value.
In the Test() method, three variables x, y, z are initialized with 1.0, 2.0 and 1.0 again.
Case 1: When you compile to a optimized release build following optimal code is generated for the three LoadInline() calls.
[SkipLocalsInit is NOT used, so at the begin of the Test() method local memory is already initialized]
Case 2: When LINE A and LINE B are both activated, following sub optimal (but working) code is produced.
The second and third call perform a redundant zero out of the struct. Interestingly the first call is still optimal. Observe the fact that the method arguments in call 1 and call 3 are the same, but produce different code.
Case 3: Even more strange - but not my real world use case - the optimizer gets totaly confused, when only LINE A is active and LINE B is commented out. The code is still working, but obviously the JIT constant branch removal has totaly failed.
Expected Behavior:
All three variants should produce the same optimal code.
Actual Behavior:
Case 1: The optimal code :-)
Case 2: For large enough structs the code and runtime behavior is very bad and seems unsymetric between the same method calls.
Case 3: This is not a real use case because a senseless Unsafe cast is made without any usage. But no warning is raised, when you accidentally compile such a source code.
Off Topic:
It would be great - especially for my use case - when a method argument could be restricted to a compile time constant.
The text was updated successfully, but these errors were encountered: