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

Revisit code that updates loops before block removal #68844

Closed
kunalspathak opened this issue May 3, 2022 · 6 comments
Closed

Revisit code that updates loops before block removal #68844

kunalspathak opened this issue May 3, 2022 · 6 comments
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Milestone

Comments

@kunalspathak
Copy link
Member

kunalspathak commented May 3, 2022

While working on #68803 to fix #68756, we encountered several issues about how we track and update loop tables around block that we will remove. We should revisit the logic in optUpdateLoopsBeforeRemoveBlock().

  1. After we renumber blocks, we add new blocks on the way (specifically during loop hoisting, we add preheaders). The lpContains() method relies on bbNum to determine if something is present in loop or not. Below is the code that is used to determine if a block is present in loop or not, but newer added blocks might have higher bbNum and can falsely claim that the block is not in the loop.
    if (loop.lpContains(auxBlock))
    {
    continue;
    }
  2. If there was a preheader which had invariants hoisted, but later those invariants were eliminated by assertion prop or some other optimization, we would be left with empty preheader which want to eliminate. We invoke optUpdateLoopsBeforeRemoveBlock() and decide if the loop should also be removed or not.
    // If `block` flows to the loop entry then the whole loop will become unreachable if it is the
    // only non-loop predecessor.

Later, we also iterate through all the blocks, one of them being the preheader that we want to eliminate. We think that it is not part of a loop (because of reason mentioned above) but then see that auxBlock->bbJumpDest == loop.lpEntry and reset the flag saying we do not have to remove the loop. Here, we assume that there was a predecessor outside the loop, but never verify if auxBlock == block.

// Check if the entry has other predecessors outside the loop.
// TODO: Replace this when predecessors are available.
for (BasicBlock* const auxBlock : Blocks())

  1. If we decide to remove the loop or not, we fail to check for certain scenarios if loop.lpHead == block and if yes, we should update the loop.lpHead to point to the predecessor of block we are trying to remove.
  2. Today, we do update loopHead in certain cases, but bbPrev might not be the right block we should set, but should be a predecesssor if possible. If we just add assert(block->bbPrev->KindIs(BBJ_NONE, BBJ_ALWAYS)), we would see that BBJ_THROW or BBJ_RETURN blocks are getting set as lpHead of a loop. This portion needs to be revisited as well.
    else if (loop.lpHead == block)
    {
    reportBefore();
    /* The loop has a new head - Just update the loop table */
    loop.lpHead = block->bbPrev;
    }

category:implementation
theme:loop-opt
skill-level:expert
cost:medium
impact:medium

@ghost ghost added the untriaged New issue has not been triaged by the area owner label May 3, 2022
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@kunalspathak
Copy link
Member Author

@dotnet/jit-contrib , @BruceForstall

@kunalspathak kunalspathak added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 3, 2022
@ghost
Copy link

ghost commented May 3, 2022

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

Issue Details

While working on #68803 to fix #68756, we encountered several issues about how we track and update loop tables around block that we will remove. We should revisit the logic in optUpdateLoopsBeforeRemoveBlock().

  1. After we renumber blocks, we add new blocks on the way (specifically during loop hoisting, we add preheaders). The lpContains() method relies on bbNum to determine if something is present in loop or not. Below is the code that is used to determine if a block is present in loop or not, but newer added blocks might have higher bbNum and can falsely claim that the block is not in the loop.
    if (loop.lpContains(auxBlock))
    {
    continue;
    }
  2. If there was a preheader which had invariants hoisted, but later those invariants were eliminated by assertion prop or some other optimization, we would be left with empty preheader which want to eliminate. We invoke optUpdateLoopsBeforeRemoveBlock() and decide if the loop should also be removed or not.
    // If `block` flows to the loop entry then the whole loop will become unreachable if it is the
    // only non-loop predecessor.

Later, we also iterate through all the blocks, one of them being the preheader that we want to eliminate. We think that it is not part of a loop (because of reason mentioned above) but then see that auxBlock->bbJumpDest == loop.lpEntry and reset the flag saying we do not have to remove the loop. Here, we assume that there was a predecessor outside the loop, but never verify if auxBlock == block.

// Check if the entry has other predecessors outside the loop.
// TODO: Replace this when predecessors are available.
for (BasicBlock* const auxBlock : Blocks())

  1. If we decide to remove the loop or not, we fail to check for certain scenarios if loop.lpHead == block and if yes, we should update the loop.lpHead to point to the predecessor of block we are trying to remove.
  2. Today, we do update loopHead in certain cases, but bbPrev might not be the right block we should set, but should be a predecesssor if possible. If we just add assert(block->bbPrev->KindIs(BBJ_NONE, BBJ_ALWAYS)), we would see that BBJ_THROW or BBJ_RETURN blocks are getting set as lpHead of a loop. This portion needs to be revisited as well.
    else if (loop.lpHead == block)
    {
    reportBefore();
    /* The loop has a new head - Just update the loop table */
    loop.lpHead = block->bbPrev;
    }
Author: kunalspathak
Assignees: -
Labels:

area-CodeGen-coreclr, untriaged

Milestone: -

@BruceForstall
Copy link
Member

I think we should enable the code that always creates loop pre-headers when doing loop recognition (you could consider it part of loop canonicalization), and prevents removing empty pre-headers through optimization, finally letting them be removed late in compilation if they are empty. This would mean there's no need to on-demand create them, which invalidates bbNum sequential ordering and thus dominators.

@BruceForstall BruceForstall added this to the Future milestone May 4, 2022
@BruceForstall BruceForstall removed the untriaged New issue has not been triaged by the area owner label May 4, 2022
@kunalspathak
Copy link
Member Author

Thanks @BruceForstall. Whenever you get a chance, can you publish a draft PR of whatever changes you have so far for "always create loop pre-headers"? I can iterate on top of it to solve this problem.

@jakobbotsch
Copy link
Member

This code no longer exists

@github-actions github-actions bot locked and limited conversation to collaborators Aug 30, 2024
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

No branches or pull requests

3 participants