-
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: fix invariant analysis for cloning #70126
Conversation
Fix some cases where the JIT was not sufficiently careful in verifing that operands in a loop were suitably invariant. Closes dotnet#61040.
Tagging subscribers to this area: @JulieLeeMSFT Issue DetailsFix some cases where the JIT was not sufficiently careful in verifing that Closes #61040.
|
@BruceForstall PTAL This is intended to have fairly minimal diffs (currently around 150 methods). Diffs will show both size decreases (because we stop cloning in some places) and size increases (because we lose a cloning condition and so either fail to remove bounds checks or fail to prove the slow loop is unreachable). Spot checking showed the diffs to be reasonable; not clear if any of them were actual bugs or just places we though we knew something that we didn't actually know. Some of the analysis here can be sharpened (eg to address #70100, and to improve the use of bit vectors, and possibly to allow some address-exposed cases). I will work on that in a follow-on change. |
|
||
if (array->OperIs(GT_LCL_VAR)) | ||
{ | ||
if (!optIsVarAssgLoop(loopInd, array->AsLclVarCommon()->GetLclNum())) |
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.
Do you want to add JITDUMP if this fails, like you did above? (Same for if it's not a GT_LCL_VAR?)
src/coreclr/jit/optimizer.cpp
Outdated
// Not clear why we return false here. We know the loop exit test | ||
// is comparing iterVar to something; we just don't know what. | ||
// | ||
// No real difference between this case and the cases above where | ||
// we don't set some kind of limit flag. | ||
// |
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.
I think the caller expects one of the LPFLG_*_LIMIT
flags to be set if this function returns true; you are currently not doing that for, e.g., cases where GT_ARR_LENGTH
fails.
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.
I think we do proper filtering later but will change this to match what we used to do.
src/coreclr/jit/optimizer.cpp
Outdated
@@ -977,7 +1001,7 @@ unsigned Compiler::optIsLoopIncrTree(GenTree* incr) | |||
// optComputeIterInfo: Check tree is loop increment of a lcl that is loop-invariant. | |||
// | |||
// Arguments: | |||
// from, to - are blocks (beg, end) which are part of the loop. | |||
// lnum - loop in question |
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.
You didn't change the actual function signature.
It's also a little weird to pass around the loop index when this function is called as part of populating the loop info, so the loop table for this loop isn't complete (I would generally assume that if given a loop index, all the info about the loop at that index was valid).
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.
You didn't change the actual function signature.
Thanks. I wanted to but ran into issues.
It's also a little weird to pass around the loop index
We've already recognized and recorded the loop, now we're just figuring out what other properties it may have. So I don't see why passing around the loop index seems problematic; it will still describe a valid loop no matter what this analysis might discover.
I'm using the index like this because the loop-based invariance analysis is more efficient and powerful than the block based invariance analysis, so I want to make as many queries as possible use the loop based checks.
The only one that can't (currently) is this check that the iterVar not have any other modifications within the loop. I may work on handling this somehow so that we only do one scan over the loop body and not two.
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.
We've already recognized and recorded the loop, now we're just figuring out what other properties it may have.
Makes sense.
src/coreclr/jit/optimizer.cpp
Outdated
{ | ||
// Parent var index is too large, assume the worst. | ||
// | ||
return false; |
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.
Shouldn't this return true
?
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.
Oops. Yes.
src/coreclr/jit/optimizer.cpp
Outdated
return optIsSetAssgLoop(lnum, vs) != 0; | ||
} | ||
else | ||
{ | ||
if (varDsc->lvIsStructField) | ||
{ | ||
return false; |
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.
Shouldn't this return true
?
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.
Ditto.
src/coreclr/jit/optimizer.cpp
Outdated
varRefKinds refs = varTypeIsGC(tree->TypeGet()) ? VR_IND_REF : VR_IND_SCL; | ||
desc->ivaMaskInd = varRefKinds(desc->ivaMaskInd | refs); |
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.
Would it be out of scope for this change to also fix cases where "treating LCL_FLD
conservatively as indirect access" is not enough, and where we can see OBJ/BLK/IND
-based stores to locals?
Test cases that expose the two issues
using System;
using System.Runtime.CompilerServices;
unsafe class LoopLocalInvariance
{
private const int ArrLen = 10;
public static int Main()
{
int[] arr = new int[ArrLen];
try
{
ProblemWithBlkAsg(arr, ArrLen);
return 101;
}
catch (IndexOutOfRangeException) { }
try
{
ProblemWithLclFldAsg(arr, ArrLen);
return 102;
}
catch (IndexOutOfRangeException) { }
return 100;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ProblemWithBlkAsg(int[] arr, int idx)
{
for (int i = 0; i < ArrLen; i++)
{
Unsafe.InitBlock(&i, (byte)idx, 1);
arr[i] = 0;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ProblemWithLclFldAsg(int[] arr, int idx)
{
for (int i = 0; i < ArrLen; i++)
{
*(byte*)&i = (byte)idx;
arr[i] = 0;
}
}
}
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.
I'll take a look, sure.
I have follow on changes where I want to enable more cloning (an in more cases) so would be good to fix all the issues we know of with the analysis.
Failure is #69474. |
Left off adding a one test case. Will tack it onto a subsequent PR. |
Fix some cases where the JIT was not sufficiently careful in verifing that
operands in a loop were suitably invariant.
Closes #61040.