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: Add loop-aware RPO, and use as LSRA's block sequence #108086

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

amanasifkhalid
Copy link
Member

Part of #107749, and follow-up to #107927. When computing a RPO of the flow graph, ensuring that the entirety of a loop body is visited before any of the loop's successors has the benefit of keeping the loop body compact in the traversal. This is certainly ideal when computing an initial block layout, and may be preferable for register allocation, too. Thus, this change formalizes loop-aware RPO creation as part of the flowgraph API surface, and uses it for LSRA's block sequence.

I plan to reuse the RPO computed during LSRA in fgDoReversePostOrderLayout once #107634 is in. To do this, I had to add a new phase check flag to disable checking basic block pre/postorder numbers, since the loop-aware RPO (or just a profile-aware RPO) won't match up with the expected DFS in our debug checks -- it seems simplest to just disable these checks altogether once we reach the backend.

@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 Sep 20, 2024
Copy link
Contributor

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

//
// Notes:
// If the flow graph has loops, the DFS will be reordered such that loop bodies are compact.
// This will invalidate BasicBlock::bbPreorderNum and BasicBlock::bbPostorderNum.
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think we have any dependencies on bbPreorderNum or bbPostorderNum in the backend, but if we want to use loop-aware RPOs elsewhere in the JIT, I can work on making these members consistent.

@amanasifkhalid
Copy link
Member Author

cc @dotnet/jit-contrib, @AndyAyersMS PTAL. I decided to only run this when optimizing since the potential codegen improvement doesn't seem to warrant the TP cost in MinOpts. Diffs show up to a 0.16% TP cost in FullOpts, so moving layout entirely to the backend and reusing the RPO computation should easily pay for this. Thanks!

@kunalspathak
Copy link
Member

Seems regression in linux/arm, windows/x86

image

@AndyAyersMS
Copy link
Member

cc @dotnet/jit-contrib, @AndyAyersMS PTAL. I decided to only run this when optimizing since the potential codegen improvement doesn't seem to warrant the TP cost in MinOpts. Diffs show up to a 0.16% TP cost in FullOpts, so moving layout entirely to the backend and reusing the RPO computation should easily pay for this. Thanks!

For min opts (at least conceptually) block order shouldn't matter, should it? There are no cross-block live registers. It would be good to verify this. If so, we might be able to save some more time in min opts by just using the linear chain order.

@amanasifkhalid
Copy link
Member Author

For min opts (at least conceptually) block order shouldn't matter, should it? There are no cross-block live registers. It would be good to verify this. If so, we might be able to save some more time in min opts by just using the linear chain order.

I think you're right. I tried this out locally, and I'm not getting any asmdiffs. I'll open a separate PR for it.

// If the flow graph has loops, the DFS will be reordered such that loop bodies are compact.
// This will invalidate BasicBlock::bbPreorderNum and BasicBlock::bbPostorderNum.
//
FlowGraphDfsTree* Compiler::fgComputeLoopAwareDfs()
Copy link
Member

@jakobbotsch jakobbotsch Oct 4, 2024

Choose a reason for hiding this comment

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

Is there anything gained from trying to represent this as an actual FlowGraphDfsTree? I think it would make more sense to have a utility function that given FlowGraphDfsTree and FlowGraphNaturalLoops visits the blocks in RPO that respects the loop structure. It would basically be a slight generalization of what we have in VN already.

The "compute DFS tree" into "identify loops" into "now create another DFS tree" seems wasteful and conceptually a bit odd.

Copy link
Member Author

Choose a reason for hiding this comment

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

Is there anything gained from trying to represent this as an actual FlowGraphDfsTree?

Probably not.

I think it would make more sense to have a utility function that given FlowGraphDfsTree and FlowGraphNaturalLoops visits the blocks in RPO that respects the loop structure.

That sounds sensible -- I'll try modeling this after FlowGraphNaturalLoop::VisitLoopBlocksReversePostOrder.

assert(blockToLoop != nullptr);

EnsureBasicBlockEpoch();
BlockSet visitedBlocks(BlockSetOps::MakeEmpty(this));
Copy link
Member

@jakobbotsch jakobbotsch Oct 8, 2024

Choose a reason for hiding this comment

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

It would be better to use the post order number traits that you can get from the DFS tree.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I should probably make a note of phasing this bbNum dependency out elsewhere.

// (first when we visit its containing loop, and then later as we iterate
// through the initial RPO).
// Thus, we need to keep track of visited blocks.
if (!BlockSetOps::IsMember(this, visitedBlocks, block->bbNum))
Copy link
Member

Choose a reason for hiding this comment

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

TryAddElemD can be used as a replacement for IsMember + AddElemD.

Comment on lines 5016 to 5020
// If this block is a loop header, visit the entire loop before moving on
if ((loop != nullptr) && (block == loop->GetHeader()))
{
loop->VisitLoopBlocksReversePostOrder(visitBlock);
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this handles nested loops properly. This needs some form of recursion, probably -- similarly to fgValueNumberBlocks.

I think this utility can be implemented without a dependency on BlockToNaturalLoopMap since FlowGraphNaturalLoops stores loops in descendant order of the header's post order number, so FlowGraphNaturalLoops::GetLoopByHeader can have an efficient binary search implementation (there is a TODO about it). It should also be possible to walk the current loop and current block in lockstep, although it seems unnecessary to go that far.

Copy link
Member Author

@amanasifkhalid amanasifkhalid Oct 8, 2024

Choose a reason for hiding this comment

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

I think this utility can be implemented without a dependency on BlockToNaturalLoopMap since FlowGraphNaturalLoops stores loops in descendant order of the header's post order number

That seems simple enough -- I'll change it.

This needs some form of recursion, probably -- similarly to fgValueNumberBlocks.

I'm guessing we can't use a lambda for the recursive logic, right? If we need to split the recursive logic into another method, would it make sense to put the loop-aware RPO logic in some sort of visitor class to hide the recursive details? I suppose I could just stick the recursive logic in some struct local to the method as well...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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.

4 participants