Skip to content
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: strengthen checking of the loop table #71184

Merged
merged 8 commits into from
Jun 28, 2022

Conversation

AndyAyersMS
Copy link
Member

Add loop table checking to the post-phase list, conditional on whether the
table is expected to be valid.

Declare that the table is valid from the end of the find loops phase to the
end of the optimization phases.

Add checks that sibling loops are fully disjoint, no child shares top with its
parent, and all top-entry loops have at most one non-loop backedge.

Closes #71084.

Add loop table checking to the post-phase list, conditional on whether the
table is expected to be valid.

Declare that the table is valid from the end of the find loops phase to the
end of the optimization phases.

Add checks that sibling loops are fully disjoint, no child shares top with its
parent, and all top-entry loops have at most one non-loop backedge.

Closes dotnet#71084.
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 23, 2022
@ghost ghost assigned AndyAyersMS Jun 23, 2022
@ghost
Copy link

ghost commented Jun 23, 2022

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

Add loop table checking to the post-phase list, conditional on whether the
table is expected to be valid.

Declare that the table is valid from the end of the find loops phase to the
end of the optimization phases.

Add checks that sibling loops are fully disjoint, no child shares top with its
parent, and all top-entry loops have at most one non-loop backedge.

Closes #71084.

Author: AndyAyersMS
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@AndyAyersMS
Copy link
Member Author

@BruceForstall PTAL
cc @dotnet/jit-contrib

This probably won't survive some of the more ambitious CI legs (like libraries-pgo).

Let's see if it gets through innerloop first.

Copy link
Member

@BruceForstall BruceForstall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! A couple nits and one major question about how often the new checking actually gets invoked.

src/coreclr/jit/fgdiagnostic.cpp Outdated Show resolved Hide resolved
src/coreclr/jit/optimizer.cpp Show resolved Hide resolved
@@ -232,6 +232,7 @@ void Phase::PostPhase(PhaseStatus status)
comp->fgDebugCheckLinks();
comp->fgDebugCheckNodesUniqueness();
comp->fgVerifyHandlerTab();
comp->fgDebugCheckLoopTable();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love having this here. (In fact, I wrote this exact change back in December but never got it in :-( ).

IIRC, not too many of the optimization phases actually go down this code path, so it won't get run much before the loop table is marked as not valid. I tried expanding the number of optimization phases that were "true" phase object phases, and used post-phase processing, and they kept hitting cases where the loop table was invalid.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VN and hoisting are dependent on proper loop structure, so it would be good to be confident that things are ok there. Let me try converting them to true phases (along with ssa and early prop)

@AndyAyersMS
Copy link
Member Author

I added proper post-phase checks for all (most?) of the phases that run between find loops and hoisting. This passes local SPMI.

Some of these newly updated phases may not really deserve dumping like we're doing now (eg fgFindOperOrder will at most reverse some subtrees but will also set costs). Currently there's no way to separate out post-phase dumping from checking.

I now see a few small SPMI diffs where we are no longer unmarking loops and so doing some alignment padding, because optOptimizeLayout is now run with loop table invalid. Presumably this might fix the issue I see in #71071, or at least related problems. I might also move the invalidation up earlier when optimizing, if the troublemaker is the update flow graph call we make in there.

@AndyAyersMS
Copy link
Member Author

/azp run runtime-coreclr jitstress

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@AndyAyersMS
Copy link
Member Author

@BruceForstall this has evolved quite a bit since you approved, can you look again?

Main thing is that many more phases opt into the default post phase checks, including all the ones that I know of that are critically dependent on the loop table being valid.

In some cases this required adding/exposing change tracking within the phases.

@AndyAyersMS
Copy link
Member Author

Also verified this addresses the AV hit in #71071 as we no longer try and unmark loops during the Optimize layout phase.

Copy link
Member

@BruceForstall BruceForstall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like we should have an assert that if a phase returns MODIFIED_NOTHING, we could actually verify that. Think: clone IR/lcl vars/EH/loop table, run phase, compare against cache. Yes, expensive and lots of new code.

#endif // DEBUG

return PhaseStatus::MODIFIED_NOTHING;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be MODIFIED_EVERYTHING?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Currently dumping and checking are tied together. I was trying to avoid redundant seeming dumps, but I suppose we want the checks.

@@ -4590,6 +4592,8 @@ void Compiler::lvaMarkLocalVars()
}
}

bool addedLocal = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be simpler to cache lvaCount here and then check lvaCount against the cached value at the end, instead of interspersing addedLocal = true calls and hoping you didn't miss any place.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be more robust. Will do.

@@ -604,6 +604,8 @@ void Compiler::optClearLoopIterInfo()
loop.lpConstInit = -1;
loop.lpTestTree = nullptr;
}

return PhaseStatus::MODIFIED_NOTHING;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, NOTHING is a misnomer. So maybe you could at least add a comment at the definition of MODIFIED_NOTHING that says what counts as something and what counts as nothing?

@@ -7645,9 +7665,6 @@ void Compiler::optHoistCandidate(GenTree* tree, BasicBlock* treeBb, unsigned lnu
return;
}

// Create a loop pre-header in which to put the hoisted code.
fgCreateLoopPreHeader(lnum);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't move this down because the next check depends on the pre-header already being created.

@@ -4840,11 +4840,13 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl

if (opts.OptimizationEnabled())
{
// xxx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix comment

@ghost ghost added the needs-author-action An issue or pull request that requires more info or actions from the author. label Jun 27, 2022
@AndyAyersMS
Copy link
Member Author

It feels like we should have an assert that if a phase returns MODIFIED_NOTHING, we could actually verify that. Think: clone IR/lcl vars/EH/loop table, run phase, compare against cache. Yes, expensive and lots of new code

We have some of this checking of this already, see Phase::Observations::Check. It seems to work pretty well in practice.

@ghost ghost removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Jun 28, 2022
@AndyAyersMS
Copy link
Member Author

@BruceForstall updated per your feedback.

@AndyAyersMS AndyAyersMS merged commit cd88b84 into dotnet:main Jun 28, 2022
@@ -6792,6 +6809,9 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt, Basi
defExec.Push(pLoopDsc->lpEntry);

optHoistLoopBlocks(lnum, &defExec, hoistCtxt);

const unsigned numHoisted = pLoopDsc->lpHoistedFPExprCount + pLoopDsc->lpHoistedExprCount;
return pLoopDsc->lpHoistAddedPreheader || (numHoisted > 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for getting back late, but what is the scenario where lpHoistAddedPreheader == true but numHoisted == 0?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that there is one scenario:

if (!BasicBlock::sameTryRegion(optLoopTable[lnum].lpHead, treeBb))
{
JITDUMP(" ... not hoisting in " FMT_LP ", eh region constraint (pre-header try index %d, candidate " FMT_BB
" try index %d\n",
lnum, optLoopTable[lnum].lpHead->bbTryIndex, treeBb->bbNum, treeBb->bbTryIndex);
return;
}

But in that case, we should just return false that no hoisting was done?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there were a few cases in SPMI where we created a preheader and then failed the eh check.

Since we've modified the flow graph (even though we might not have hoisted anything) we need to return true.

@@ -5958,17 +5960,10 @@ class Compiler

// Do hoisting of all loops nested within loop "lnum" (an index into the optLoopTable), followed
// by the loop "lnum" itself.
//
// "m_pHoistedInCurLoop" helps a lot in eliminating duplicate expressions getting hoisted
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this comment was deleted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like an inadvertent edit -- at one point I had modified the signature of optHoistLoopNest.

@ghost ghost locked as resolved and limited conversation to collaborators Jul 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[JIT] Update loop integrity checks
3 participants