Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2089,7 +2089,8 @@ struct NaturalLoopIterInfo
// loop can reach every other block of the loop.
//
// * All loop blocks are dominated by the header block, i.e. the header block
// is guaranteed to be entered on every iteration.
// is guaranteed to be entered on every iteration. Note that in the prescence
// of exceptional flow the header might not fully execute on every iteration.
//
// * From the above it follows that the loop can only be entered at the header
// block. FlowGraphNaturalLoop::EntryEdges() gives a vector of these edges.
Expand Down
29 changes: 14 additions & 15 deletions src/coreclr/jit/optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6136,11 +6136,6 @@ PhaseStatus Compiler::optHoistLoopCode()
LoopHoistContext hoistCtxt(this);
for (FlowGraphNaturalLoop* loop : m_loops->InPostOrder())
{
if (!loop->GetHeader()->HasFlag(BBF_OLD_LOOP_HEADER_QUIRK))
{
continue;
}

#if LOOP_HOIST_STATS
// Record stats
m_curLoopHasHoistedExpression = false;
Expand Down Expand Up @@ -6341,11 +6336,6 @@ bool Compiler::optHoistThisLoop(FlowGraphNaturalLoop* loop, LoopHoistContext* ho

for (FlowGraphNaturalLoop* childLoop = loop->GetChild(); childLoop != nullptr; childLoop = childLoop->GetSibling())
{
if (!childLoop->GetHeader()->HasFlag(BBF_OLD_LOOP_HEADER_QUIRK))
{
continue;
}

assert(childLoop->EntryEdges().size() == 1);
BasicBlock* childPreHead = childLoop->EntryEdge(0)->getSourceBlock();
if (loop->ExitEdges().size() == 1)
Expand Down Expand Up @@ -6376,17 +6366,26 @@ bool Compiler::optHoistThisLoop(FlowGraphNaturalLoop* loop, LoopHoistContext* ho
", or pre-headers of nested loops, if any:\n",
exiting->bbNum);

// Push dominators, until we reach "entry" or exit the loop.

// Push dominators, until we reach the header or exit the loop.
//
// Note that there is a mismatch between the dominator tree dominance
// and loop header dominance; the dominator tree dominance relation
// guarantees that a block A that dominates B was exited before B is
// entered, meaning it could not possibly have thrown an exception. On
// the other hand loop finding guarantees only that the header was
// entered before other blocks in the loop. If the header is a
// try-begin then blocks inside the catch may not necessarily be fully
// dominated by the header, but may still be part of the loop.
Comment on lines +6371 to +6378
Copy link
Member Author

@jakobbotsch jakobbotsch Jan 10, 2024

Choose a reason for hiding this comment

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

This is something we could consider canonicalizing, though I'm not sure it is really necessary (I would expect most reasoning about the header to already need to take into account that only the "beginning" of it is guaranteed to be executed).

An example that hits the assert in the base looks like:

private static int Foo(int[] arr, int n)
{
    int sum = 0;
    for (int i = 0; i < 100; i++)
    {
        try
        {
            sum += arr[i];
        }
        catch (IndexOutOfRangeException)
        {
        }
    }

    return sum;
}

I think we should be able to recognize and optimize loops like this. The flow graph looks like this:

-----------------------------------------------------------------------------------------------------------------------------------------
BBnum BBid ref try hnd preds           weight    lp [IL range]     [jump]      [EH region]         [flags]
-----------------------------------------------------------------------------------------------------------------------------------------
BB01 [0000]  1                             1       [000..006)-> BB05 ( cond )                     i 
BB02 [0008]  1       BB01                  1       [006..???)-> BB03 (always)                     internal LoopPH q 
BB03 [0002]  2  0    BB02,BB04             4       [006..00F)-> BB04 (always) T0      try { }     i keep Loop idxlen bwd q 
BB04 [0004]  2       BB03,BB06             8       [012..01B)-> BB03 ( cond )                     i bwd 
BB05 [0006]  2       BB01,BB04             1       [01B..01D)        (return)                     i 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ funclets follow
BB06 [0003]  1     0                       0       [00F..012)-> BB04 ( cret )    H0 F catch { }   i rare keep xentry flet bwd 
-----------------------------------------------------------------------------------------------------------------------------------------

...

L00 header: BB03
  Members (3): [BB03..BB04];BB06
  Entry: BB02 -> BB03
  Exit: BB04 -> BB05
  Back: BB04 -> BB03

BB06 is the catch block; it is part of the loop but its immediate dominator is BB02, since BB03 (the header) is not guaranteed to be exited before BB06 is entered.

I think loops like these are definitely ones we want to be able to recognize and handle. If we run into more odd special casing then I think we can canonicalize these cases by introducing a block before the "try" begin, so that the try begin does not become the header. (Note that a loop-inside-try case could also have the try-begin as the header, but would not have the catch blocks considered as part of the loop, so we would want to differentiate this case in the canonicalization.)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can canonicalize these cases by introducing a block before the "try" begin, so that the try begin does not become the header

I like that idea. Removing cases where we pessimize due to difficult EH flow graph structures is a good thing.

//
BasicBlock* cur = exiting;
while ((cur != nullptr) && (cur != loop->GetHeader()))
while ((cur != nullptr) && (cur != loop->GetHeader()) && loop->ContainsBlock(cur))
{
JITDUMP(" -- " FMT_BB " (dominate exit block)\n", cur->bbNum);
assert(loop->ContainsBlock(cur));
defExec.Push(cur);
cur = cur->bbIDom;
}
noway_assert(cur == loop->GetHeader());

assert((cur == loop->GetHeader()) || bbIsTryBeg(loop->GetHeader()));
}
else // More than one exit
{
Expand Down