-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RyuJIT: loads for static readonly field should have the same VN #38201
Comments
using System;
using System.Runtime.CompilerServices;
using System.Threading;
public class Program
{
static void Main()
{
// emulate tier0 -> tier1 compilation
// thus tier1 happens when `Program` is statically inited
for (int i = 0; i < 200; i++)
{
Foo(0);
Thread.Sleep(15);
}
Console.ReadKey();
}
static readonly int[] s_Array = { 1, 2, 3, 4 };
[MethodImpl(MethodImplOptions.NoInlining)]
static int Foo(int i)
{
if ((uint)s_Array.Length > (uint)i)
return s_Array[i];
return 0;
}
} Current codegen with TieredCompilation disabled for
|
Yes, this should be safe. Note the array length field is special. You should verify that the array elements are still considered variant: private static readonly int[] s_Array = new int[] {1, 2, 3, 4};
static int Foo1()
{
int sum = 0;
for (int i = 0; i < s_Array.Length; i++)
sum += s_Array[i] + s_Array[i] + s_Array[0]; // no cse or hoist of the element access
return sum;
} |
Ah, right. |
e.g.: private static readonly Guid v1 = Guid.NewGuid();
[MethodImpl(MethodImplOptions.NoInlining)]
static bool Foo(int i)
{
return v1 == v1;
} G_M14484_IG01:
4883EC48 sub rsp, 72
C5F877 vzeroupper
;; bbWeight=1 PerfScore 1.25
G_M14484_IG02:
48B9782C0010F8010000 mov rcx, 0x1F810002C78
488B09 mov rcx, gword ptr [rcx]
C5FA6F4108 vmovdqu xmm0, xmmword ptr [rcx+8]
C5FA7F442438 vmovdqu xmmword ptr [rsp+38H], xmm0
48B9782C0010F8010000 mov rcx, 0x1F810002C78 ;; dup!
488B09 mov rcx, gword ptr [rcx] ;; dup!
C5FA6F4108 vmovdqu xmm0, xmmword ptr [rcx+8] ;; dup!
C5FA7F442428 vmovdqu xmmword ptr [rsp+28H], xmm0
488D4C2438 lea rcx, bword ptr [rsp+38H]
488D542428 lea rdx, bword ptr [rsp+28H]
E82A70FFFF call System.Guid:op_Equality(System.Guid,System.Guid):bool
90 nop
;; bbWeight=1 PerfScore 12.75
G_M14484_IG03:
4883C448 add rsp, 72
C3 ret |
jit-diff (--pmi --cctors) is quite promising:
|
Nice improvements. |
Unfortunately, no -- instance fields can still be modified at runtime. |
@EgorBo Did you want to put up your PR for this issue? |
@briansull will take a look tomorrow |
I am sure there are similar issues but mine comes with a possible fix, let me explain the problem via the following example:
Some of us for sure know that bound check elimination won't work here and you have to save
s_Array
to a local variable first.The bound checks won't be removed even for a canonical
for
loop (but works forforeach
since it saves the field to a local first - see Question: Why for loop is 1.3 slower over byte[] than foreach):It happens because JIT assigns different VN for loads, e.g.:
IR after
fgValueNumber
forFoo()
:^ As you can see both IND have different VNs (140 and 141) thus we see a redundant memory load in the final codegen:
So what if we can tell the jit that if that field is
static readonly
and the type is already statically inited (e.g. when we recompileFoo
after a while to promote it to Tier1 from Tier0) we can mark such indirects as invariant? Here is my fix: EgorBo@76d919cand here is the new codegen for the sample above ^:
As you can see there are no redundant loads here any more. The bound checks are also removed correctly for the other samples above with this fix.
So is it safe to assume such readonly fields are invariant? @dotnet/jit-contrib @jkotas ?
category:cq
theme:value-numbering
skill-level:intermediate
cost:small
The text was updated successfully, but these errors were encountered: