-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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: Try to maintain fallthrough in Compiler::fgSplitEdge
#107419
base: main
Are you sure you want to change the base?
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
cc @dotnet/jit-contrib, @AndyAyersMS PTAL. Diffs are large, though they're inflated by collections with tiering ( |
Seems like perhaps That being said, I have seen those LSRA blocks end up very poorly placed, and we might question whether after source or before target is the right location for the new block, when source and target blocks are not adjacent, or whether we just (as we've discussed) redo layout if LSRA makes changes. |
I suppose we could differentiate between before/after layout, though since we're seeing this block placement affect FullOpts with some frequency, it might be more worthwhile to get layout working after LSRA.
Between the two, I think placing after source is better most of the time? If the target has multiple preds, placing before target could potentially break up hotter fallthrough. If the source has only one successor, then placing after the source doesn't change anything, but if the source is a If you'd like, I can put this PR on-hold and work on getting layout working after LSRA. For that, I'm thinking we can continue to run layout before LSRA, and detect if we need to run it again after, just to minimize regressions. Once we've refactored switch recognition to not depend on lexical layout (#107076), we should be able to only run layout once, after LSRA. |
I think it makes sense to look into fixing layout post LSRA first. If that turns out to be complicated, then perhaps we can reconsider and do this first. I believe LSRA will only split "critical edges"— edges where the source has multiple flow successors and the target has multiple flow predecessors. If we trust the initial layout then if source and target are adjacent then putting the block in between is the right thing; if not, presumably both source and target have other preferred partners and if those partners are adjacent then the new block should be placed out of the way somewhere where it won't break up any other important flow (though not necessarily at the end of the method / region, which is what I commonly see). If the preferred source or target partner is not adjacent (or maybe if no successor/predecessor is adjacent), then we should be placing after source or before target depending. This calculus would be easier if we hand the flow scoring that we are envisioning for k-opt, as we could evaluate the different options and pick the one that has the best score. |
With #107483 merged, the diffs are still big, though FullOpts diffs are concentrated in @AndyAyersMS are you ok taking this change as-is, or would you prefer we whittle down the churn as much as possible? |
Given some branch
curr -> succ
,fgSplitEdge
introduces an intermediary block to create the formcurr -> newBlock -> succ
. The lexical placement ofnewBlock
wouldn't matter if it weren't for the fact that we callfgSplitEdge
during LSRA, after we've reordered blocks. Ideally, we wouldn't introduce any new blocks after establishing layout, but for the time being, always placingnewBlock
aftercurr
to create fallthrough -- effectively moving thecurr -> succ
branch up a block, and not introducing new branches -- seems to be a decent compromise.