Skip to content

Commit

Permalink
Loop refactoring and commenting improvements
Browse files Browse the repository at this point in the history
- Remove unneeded FIRST concept in loop table; it was always equal to TOP.
- Rename optMarkLoopsBlocks to optScaleLoopBlocks to more accurately describe
what it does. More consistently report block scaling in the dump
- Create optMarkLoopHeads. This was refactored out of fgRemoveUnreachableBlocks so
it can be called in a more logical location (at the beginning of optFindLoops),
and only does one thing.
- fgMakeOutgoingStructArgCopy: remove unused `argIndex` argument; reorder calls to
fgMightHaveLoop.
- Update and write a bunch of comments; convert away from `/* */` style comments.
  • Loading branch information
BruceForstall committed Nov 13, 2021
1 parent aae8f51 commit 4d855bc
Show file tree
Hide file tree
Showing 12 changed files with 437 additions and 472 deletions.
9 changes: 3 additions & 6 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4877,14 +4877,12 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
//
DoPhase(this, PHASE_COMPUTE_REACHABILITY, &Compiler::fgComputeReachability);

// Discover and classify natural loops
// (e.g. mark iterative loops as such). Also marks loop blocks
// and sets bbWeight to the loop nesting levels
// Discover and classify natural loops (e.g. mark iterative loops as such). Also marks loop blocks
// and sets bbWeight to the loop nesting levels.
//
DoPhase(this, PHASE_FIND_LOOPS, &Compiler::optFindLoops);

// Clone loops with optimization opportunities, and
// choose one based on dynamic condition evaluation.
// Clone loops with optimization opportunities, and choose one based on dynamic condition evaluation.
//
DoPhase(this, PHASE_CLONE_LOOPS, &Compiler::optCloneLoops);

Expand Down Expand Up @@ -8675,7 +8673,6 @@ void cLoop(Compiler* comp, Compiler::LoopDsc* loop)
static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
printf("===================================================================== Loop %u\n", sequenceNumber++);
printf("HEAD " FMT_BB "\n", loop->lpHead->bbNum);
printf("FIRST " FMT_BB "\n", loop->lpFirst->bbNum);
printf("TOP " FMT_BB "\n", loop->lpTop->bbNum);
printf("ENTRY " FMT_BB "\n", loop->lpEntry->bbNum);
if (loop->lpExitCnt == 1)
Expand Down
49 changes: 22 additions & 27 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6324,10 +6324,7 @@ class Compiler
GenTreeCall* fgMorphArgs(GenTreeCall* call);
GenTreeArgList* fgMorphArgList(GenTreeArgList* args, MorphAddrContext* mac);

void fgMakeOutgoingStructArgCopy(GenTreeCall* call,
GenTreeCall::Use* args,
unsigned argIndex,
CORINFO_CLASS_HANDLE copyBlkClass);
void fgMakeOutgoingStructArgCopy(GenTreeCall* call, GenTreeCall::Use* args, CORINFO_CLASS_HANDLE copyBlkClass);

GenTree* fgMorphLocalVar(GenTree* tree, bool forceRemorph);

Expand Down Expand Up @@ -6714,18 +6711,16 @@ class Compiler
// bbNext order) sequence of basic blocks. (At times, we may require the blocks in a loop to be "properly numbered"
// in bbNext order; we use comparisons on the bbNum to decide order.)
// The blocks that define the body are
// first <= top <= entry <= bottom .
// top <= entry <= bottom
// The "head" of the loop is a block outside the loop that has "entry" as a successor. We only support loops with a
// single 'head' block. The meanings of these blocks are given in the definitions below. Also see the picture at
// Compiler::optFindNaturalLoops().
struct LoopDsc
{
BasicBlock* lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor.
BasicBlock* lpFirst; // FIRST block (in bbNext order) reachable within this loop. (May be part of a nested
// loop, but not the outer loop.)
BasicBlock* lpTop; // loop TOP (the back edge from lpBottom reaches here) (in most cases FIRST and TOP are the
// same)
BasicBlock* lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM)
BasicBlock* lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor.
BasicBlock* lpTop; // loop TOP (the back edge from lpBottom reaches here). Lexically first block (in bbNext
// order) reachable in this loop.
BasicBlock* lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM)
BasicBlock* lpBottom; // loop BOTTOM (from here we have a back edge to the TOP)
BasicBlock* lpExit; // if a single exit loop this is the EXIT (in most cases BOTTOM)

Expand Down Expand Up @@ -6826,69 +6821,68 @@ class Compiler
// Returns "true" iff "*this" contains the blk.
bool lpContains(BasicBlock* blk) const
{
return lpFirst->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum;
return lpTop->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum;
}
// Returns "true" iff "*this" (properly) contains the range [first, bottom] (allowing firsts
// to be equal, but requiring bottoms to be different.)
bool lpContains(BasicBlock* first, BasicBlock* bottom) const
{
return lpFirst->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum;
return lpTop->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum;
}

// Returns "true" iff "*this" (properly) contains "lp2" (allowing firsts to be equal, but requiring
// bottoms to be different.)
bool lpContains(const LoopDsc& lp2) const
{
return lpContains(lp2.lpFirst, lp2.lpBottom);
return lpContains(lp2.lpTop, lp2.lpBottom);
}

// Returns "true" iff "*this" is (properly) contained by the range [first, bottom]
// (allowing firsts to be equal, but requiring bottoms to be different.)
bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) const
{
return first->bbNum <= lpFirst->bbNum && lpBottom->bbNum < bottom->bbNum;
return first->bbNum <= lpTop->bbNum && lpBottom->bbNum < bottom->bbNum;
}

// Returns "true" iff "*this" is (properly) contained by "lp2"
// (allowing firsts to be equal, but requiring bottoms to be different.)
bool lpContainedBy(const LoopDsc& lp2) const
{
return lpContains(lp2.lpFirst, lp2.lpBottom);
return lpContains(lp2.lpTop, lp2.lpBottom);
}

// Returns "true" iff "*this" is disjoint from the range [top, bottom].
bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) const
{
return bottom->bbNum < lpFirst->bbNum || lpBottom->bbNum < first->bbNum;
return bottom->bbNum < lpTop->bbNum || lpBottom->bbNum < first->bbNum;
}
// Returns "true" iff "*this" is disjoint from "lp2".
bool lpDisjoint(const LoopDsc& lp2) const
{
return lpDisjoint(lp2.lpFirst, lp2.lpBottom);
return lpDisjoint(lp2.lpTop, lp2.lpBottom);
}
// Returns "true" iff the loop is well-formed (see code for defn).
bool lpWellFormed() const
{
return lpFirst->bbNum <= lpTop->bbNum && lpTop->bbNum <= lpEntry->bbNum &&
lpEntry->bbNum <= lpBottom->bbNum &&
return lpTop->bbNum <= lpEntry->bbNum && lpEntry->bbNum <= lpBottom->bbNum &&
(lpHead->bbNum < lpTop->bbNum || lpHead->bbNum > lpBottom->bbNum);
}

// LoopBlocks: convenience method for enabling range-based `for` iteration over all the
// blocks in a loop, e.g.:
// for (BasicBlock* const block : loop->LoopBlocks()) ...
// Currently, the loop blocks are expected to be in linear, lexical, `bbNext` order
// from `lpFirst` through `lpBottom`, inclusive. All blocks in this range are considered
// from `lpTop` through `lpBottom`, inclusive. All blocks in this range are considered
// to be part of the loop.
//
BasicBlockRangeList LoopBlocks() const
{
return BasicBlockRangeList(lpFirst, lpBottom);
return BasicBlockRangeList(lpTop, lpBottom);
}
};

protected:
bool fgMightHaveLoop(); // returns true if there are any backedges
bool fgMightHaveLoop(); // returns true if there are any back edges
bool fgHasLoops; // True if this method has any loops, set in fgComputeReachability

public:
Expand All @@ -6901,7 +6895,6 @@ class Compiler
#endif // DEBUG

bool optRecordLoop(BasicBlock* head,
BasicBlock* first,
BasicBlock* top,
BasicBlock* entry,
BasicBlock* bottom,
Expand All @@ -6917,7 +6910,6 @@ class Compiler
#ifdef DEBUG
void optPrintLoopInfo(unsigned loopNum,
BasicBlock* lpHead,
BasicBlock* lpFirst,
BasicBlock* lpTop,
BasicBlock* lpEntry,
BasicBlock* lpBottom,
Expand All @@ -6930,9 +6922,12 @@ class Compiler
void optCheckPreds();
#endif

// Determine if there are any potential loops, and set BBF_LOOP_HEAD on potential loop heads.
void optMarkLoopHeads();

void optSetBlockWeights();

void optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool excludeEndBlk);
void optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk);

void optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk);

Expand Down Expand Up @@ -8290,7 +8285,7 @@ class Compiler

static fgWalkPreFn CountSharedStaticHelper;
static bool IsSharedStaticHelper(GenTree* tree);
static bool IsGcSafePoint(GenTree* tree);
static bool IsGcSafePoint(GenTreeCall* call);

static CORINFO_FIELD_HANDLE eeFindJitDataOffs(unsigned jitDataOffs);
// returns true/false if 'field' is a Jit Data offset
Expand Down
38 changes: 17 additions & 21 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3674,34 +3674,30 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree)
return result1;
}

inline bool Compiler::IsGcSafePoint(GenTree* tree)
inline bool Compiler::IsGcSafePoint(GenTreeCall* call)
{
if (tree->IsCall())
if (!call->IsFastTailCall())
{
GenTreeCall* call = tree->AsCall();
if (!call->IsFastTailCall())
if (call->IsUnmanaged() && call->IsSuppressGCTransition())
{
if (call->IsUnmanaged() && call->IsSuppressGCTransition())
{
// Both an indirect and user calls can be unmanaged
// and have a request to suppress the GC transition so
// the check is done prior to the separate handling of
// indirect and user calls.
return false;
}
else if (call->gtCallType == CT_INDIRECT)
// Both an indirect and user calls can be unmanaged
// and have a request to suppress the GC transition so
// the check is done prior to the separate handling of
// indirect and user calls.
return false;
}
else if (call->gtCallType == CT_INDIRECT)
{
return true;
}
else if (call->gtCallType == CT_USER_FUNC)
{
if ((call->gtCallMoreFlags & GTF_CALL_M_NOGCCHECK) == 0)
{
return true;
}
else if (call->gtCallType == CT_USER_FUNC)
{
if ((call->gtCallMoreFlags & GTF_CALL_M_NOGCCHECK) == 0)
{
return true;
}
}
// otherwise we have a CT_HELPER
}
// otherwise we have a CT_HELPER
}

return false;
Expand Down
29 changes: 17 additions & 12 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4656,17 +4656,15 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)

if (fgDomsComputed && fgReachable(succBlock, block))
{
/* Mark all the reachable blocks between 'succBlock' and 'block', excluding 'block' */
optMarkLoopBlocks(succBlock, block, true);
// Mark all the reachable blocks between 'succBlock' and 'bPrev'
optScaleLoopBlocks(succBlock, bPrev);
}
}
else if (succBlock->isLoopHead() && bPrev && (succBlock->bbNum <= bPrev->bbNum))
{
skipUnmarkLoop = true;
}

noway_assert(succBlock);

// If this is the first Cold basic block update fgFirstColdBlock
if (block == fgFirstColdBlock)
{
Expand Down Expand Up @@ -4709,12 +4707,6 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
succBlock->bbRefs++;
}

fgUnlinkBlock(block);

/* mark the block as removed and set the change flag */

block->bbFlags |= BBF_REMOVED;

/* Update bbRefs and bbPreds.
* All blocks jumping to 'block' now jump to 'succBlock'.
* First, remove 'block' from the predecessor list of succBlock.
Expand Down Expand Up @@ -4804,6 +4796,9 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
break;
}
}

fgUnlinkBlock(block);
block->bbFlags |= BBF_REMOVED;
}

if (bPrev != nullptr)
Expand Down Expand Up @@ -5564,7 +5559,17 @@ BasicBlock* Compiler::fgRelocateEHRange(unsigned regionIndex, FG_RELOCATE_TYPE r
return bLast;
}

// return true if there is a possibility that the method has a loop (a backedge is present)
//------------------------------------------------------------------------
// fgMightHaveLoop: return true if there is a possibility that the method has a loop (a back edge is present).
// This function doesn't depend on any previous loop computations, including predecessors. It looks for any
// lexical back edge to a block previously seen in a forward walk of the block list.
//
// As it walks all blocks and all successors of each block (including EH successors), it is not cheap.
// It returns as soon as any possible loop is discovered.
//
// Return Value:
// true if there might be a loop
//
bool Compiler::fgMightHaveLoop()
{
// Don't use a BlockSet for this temporary bitset of blocks: we don't want to have to call EnsureBasicBlockEpoch()
Expand All @@ -5577,7 +5582,7 @@ bool Compiler::fgMightHaveLoop()
{
BitVecOps::AddElemD(&blockVecTraits, blocksSeen, block->bbNum);

for (BasicBlock* succ : block->GetAllSuccs(this))
for (BasicBlock* const succ : block->GetAllSuccs(this))
{
if (BitVecOps::IsMember(&blockVecTraits, blocksSeen, succ->bbNum))
{
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1632,7 +1632,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos)
}
}

// Add regions for the loops. Note that loops are assumed to be contiguous from `lpFirst` to `lpBottom`.
// Add regions for the loops. Note that loops are assumed to be contiguous from `lpTop` to `lpBottom`.

if (includeLoops)
{
Expand All @@ -1645,7 +1645,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos)
continue;
}
sprintf_s(name, sizeof(name), FMT_LP, loopNum);
rgnGraph.Insert(name, RegionGraph::RegionType::Loop, loop.lpFirst, loop.lpBottom);
rgnGraph.Insert(name, RegionGraph::RegionType::Loop, loop.lpTop, loop.lpBottom);
}
}

Expand Down
Loading

0 comments on commit 4d855bc

Please sign in to comment.