-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
JIT: Generalize strategy for finding addrecs #99048
Conversation
When looking at a PHI, utilize a strategy where a symbolic node representing the recurrence is inserted in the cache, after which the analysis is invoked recursively. The created SCEV can afterwards be turned into a recurrence by considering the symbolic node to be a recursive occurrence.
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsWhen looking at a PHI, utilize a strategy where a symbolic node representing the recurrence is inserted in the cache, after which the analysis is invoked recursively. The created SCEV can afterwards be turned into a recurrence by considering the symbolic node to be a recursive occurrence.
|
FYI @dotnet/jit-contrib... From the diffs I'm not sure the complexity is worth it. Seems we recognize and widen around 1400 more primary IVs across the collections. I might try to compare this with a simpler approach that only follows SSA defs but otherwise sticks with the "simple" add recs only. |
How hard is it to back-map these to the C# for the methods? I am curious if these cases can arise from vanilla C# or if they're from various bespoke loops. Failing to recognize primary IVs may eventually lock us out of other optimizations. |
Some of the cases I looked at were due to CSE -- for example, if the loop body has an occurrence of There are also cases we still do not get even with this PR, and those arise even from standard looking loops. I left a comment about one of them in the code, but that case arises from this loop: runtime/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs Lines 534 to 548 in 806d11e
STMT00005 ( 0x04A[E-] ... ??? )
[000020] DA--------- ▌ STORE_LCL_VAR int V07 tmp1
[000019] ----------- └──▌ SUB int
[000017] ----------- ├──▌ LCL_VAR int V04 loc1
[000018] ----------- └──▌ CNS_INT int 1
Marked V07 as a single def local
[ 2] 78 (0x04e) stloc.1
STMT00006 ( ??? ... ??? )
[000023] DA--------- ▌ STORE_LCL_VAR int V04 loc1
[000022] ----------- └──▌ LCL_VAR int V07 tmp1
[ 1] 79 (0x04f) ldc.i4.0 0
[ 2] 80 (0x050) bge.s
STMT00007 ( ??? ... ??? )
[000026] ----------- ▌ JTRUE void
[000025] ----------- └──▌ GE int
[000021] ----------- ├──▌ LCL_VAR int V07 tmp1
[000024] ----------- └──▌ CNS_INT int 0 later on local copy prop comes along and creates more uses of Bucket[] lbuckets = _buckets;
int i = lbuckets.Length;
int j = i - 1;
i = j;
if (j >= 0)
{
while (true)
{
object? keyv = lbuckets[j].key; // local copy prop changed i to j, making j and i both into IVs because of loop inversion
if ((keyv != null) && (keyv != _buckets))
{
array.SetValue(keyv, arrayIndex++);
}
j = i - 1;
i = j;
if (j < 0)
break;
}
} and we aren't able to analyze that I collected some more stats: over ASP.NET we can analyze 1202 header PHIs into add recs that we couldn't analyze before. There are 30970 loops found in the loop finding phase, so that's IVs in around 3.8% of all loops. |
cc @dotnet/jit-contrib PTAL @BruceForstall @AndyAyersMS With this we are able to analyze both the examples that the comment I removed covered. For example: [MethodImpl(MethodImplOptions.NoInlining)]
public static int Foo(int[] arr)
{
int sum = 0;
int i = 0;
int j = 0;
while (i < arr.Length)
{
sum += arr[i];
i += j;
j++;
}
return sum;
} STMT00009 ( ??? ... ??? )
N004 ( 0, 0) [000049] DA--------- ▌ STORE_LCL_VAR int V02 loc1 d:3 $VN.Void
N003 ( 0, 0) [000048] ----------- └──▌ PHI int $242
N001 ( 0, 0) [000062] ----------- pred BB03 ├──▌ PHI_ARG int V02 loc1 u:4
N002 ( 0, 0) [000059] ----------- pred BB02 └──▌ PHI_ARG int V02 loc1 u:2 $c0
=> <L00, V02.2 (0), <L00, V03.2 (0), 1>> (a chain of recurrences, where the step value itself evolves). Another case: int sum = 0;
int i = 0;
while (i < arr.Length)
{
sum += arr[i++];
sum += arr[i++];
sum += arr[i++];
sum += arr[i++];
}
return sum; STMT00017 ( ??? ... ??? )
N004 ( 0, 0) [000123] DA--------- ▌ STORE_LCL_VAR int V02 loc1 d:3 $VN.Void
N003 ( 0, 0) [000122] ----------- └──▌ PHI int $241
N001 ( 0, 0) [000132] ----------- pred BB03 ├──▌ PHI_ARG int V02 loc1 u:7
N002 ( 0, 0) [000130] ----------- pred BB02 └──▌ PHI_ARG int V02 loc1 u:2 $c0
=> <L00, V02.2 (0), 4> |
Since you have the VNs for the initial values and steps, can't you pose this as a VN equality check? Basically ask if the VN for the init and step of one is the same as the corresponding VNs for the other? |
// and turning the recursive result into an addrec. | ||
// | ||
return CreateSimpleAddRec(store, enterScev, ssaDsc->GetBlock(), ssaDsc->GetDefNode()->Data()); | ||
Scev* simpleAddRec = CreateSimpleAddRec(store, enterScev, ssaDsc->GetBlock(), ssaDsc->GetDefNode()->Data()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be useful to retain the prior comment here and augment so there's as a worked-out example of how these symbolic addrecs end up getting resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense... let me add it as part of a future PR.
Something like that may be possible.. I need to think about what kind of manipulation is legal on these "temporary" scevs that may or may not have recursive occurrences. A priori it's not so clear to me -- not seeing any recursive appearance of the SCEV would typically mean the IV is of some unrepresentable shape (e.g. a flip-flop). |
When looking at a PHI, utilize a strategy where a symbolic node representing the recurrence is inserted in the cache, after which the analysis is invoked recursively. The created SCEV can afterwards be turned into a recurrence by considering the symbolic node to be a recursive occurrence.