Skip to content

Commit 18cfe65

Browse files
committed
JIT: move return merging earlier
Instead of merging returns to the common return block in morph, do all the merging in `fgAddInternal` (where we already did some merging). This removes a case where morph would add a control flow edge in a way that might disrupt an ongoing RPO. Earlier merging also opens up the possibility of tail merging some of the copies into the canonical return local, and possibly even some of the computations that feed the copies. Modify the flow alterations done by morph. Previously if a tail call was expressed via a call to a `CORINFO_TAILCALL_HELPER`, morph would change the block kind to `BBJ_RETURN` and then merge the return, changing the block kind to `BBJ_ALWAYS`. Since merging now happens before moprh, morph needs to leave the block kind alone. Generalize the post-tail-call sanity check in morph to recognize one new case that can come up. Contributes to dotnet#93246.
1 parent 05eacdc commit 18cfe65

File tree

3 files changed

+224
-191
lines changed

3 files changed

+224
-191
lines changed

src/coreclr/jit/compiler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -4805,7 +4805,7 @@ class Compiler
48054805
void fgMorphStmts(BasicBlock* block);
48064806
void fgMorphBlocks();
48074807

4808-
void fgMergeBlockReturn(BasicBlock* block);
4808+
bool fgMergeBlockReturn(BasicBlock* block);
48094809

48104810
bool fgMorphBlockStmt(BasicBlock* block, Statement* stmt DEBUGARG(const char* msg));
48114811
void fgMorphStmtBlockOps(BasicBlock* block, Statement* stmt);

src/coreclr/jit/flowgraph.cpp

+139-3
Original file line numberDiff line numberDiff line change
@@ -2367,8 +2367,13 @@ class MergedReturns
23672367
assert(searchLimit < maxReturns);
23682368
mergedReturnBlock = CreateReturnBB(searchLimit);
23692369
comp->genReturnBB = mergedReturnBlock;
2370-
// Downstream code expects the `genReturnBB` to always remain
2371-
// once created, so that it can redirect flow edges to it.
2370+
2371+
// In principle we should be able to remove the proctection
2372+
// we add here, and remove the genReturnBB if it becomes unreachable,
2373+
// since we now introduce all the flow to it during this phase.
2374+
//
2375+
// But we'll keep this as is for now.
2376+
//
23722377
mergedReturnBlock->bbFlags |= BBF_DONT_REMOVE;
23732378
}
23742379
}
@@ -2471,10 +2476,11 @@ class MergedReturns
24712476
// Notes:
24722477
// * rewrites shared generic catches in to filters
24732478
// * adds code to handle modifiable this
2474-
// * determines number of epilogs and merges returns
2479+
// * determines number of epilogs and merges "same constant" returns
24752480
// * does special setup for pinvoke/reverse pinvoke methods
24762481
// * adds callouts and EH for synchronized methods
24772482
// * adds just my code callback
2483+
// * merges returns to common return block if necessary
24782484
//
24792485
// Returns:
24802486
// Suitable phase status.
@@ -2752,6 +2758,20 @@ PhaseStatus Compiler::fgAddInternal()
27522758
madeChanges = true;
27532759
}
27542760

2761+
if (genReturnBB != nullptr)
2762+
{
2763+
for (BasicBlock* const block : Blocks())
2764+
{
2765+
// We can see BBF_HAS_JMP from CEE_JMP importation. Exclude such here
2766+
//
2767+
if ((block != genReturnBB) && block->KindIs(BBJ_RETURN) && ((block->bbFlags & BBF_HAS_JMP) == 0))
2768+
{
2769+
fgMergeBlockReturn(block);
2770+
madeChanges = true;
2771+
}
2772+
}
2773+
}
2774+
27552775
#ifdef DEBUG
27562776
if (verbose)
27572777
{
@@ -2764,6 +2784,122 @@ PhaseStatus Compiler::fgAddInternal()
27642784
return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
27652785
}
27662786

2787+
//------------------------------------------------------------------------
2788+
// fgMergeBlockReturn: assign the block return value (if any) into the single return temp
2789+
// and branch to the single return block.
2790+
//
2791+
// Arguments:
2792+
// block - the block to process.
2793+
//
2794+
// Returns:
2795+
// true if block was modified to branch to the single return block
2796+
//
2797+
// Notes:
2798+
// A block is not guaranteed to have a last stmt if its jump kind is BBJ_RETURN.
2799+
// For example a method returning void could have an empty block with jump kind BBJ_RETURN.
2800+
// Such blocks do materialize as part of in-lining.
2801+
//
2802+
// A block with jump kind BBJ_RETURN does not necessarily need to end with GT_RETURN.
2803+
// It could end with a tail call or rejected tail call or monitor.exit or a GT_INTRINSIC.
2804+
// For now it is safe to explicitly check whether last stmt is GT_RETURN if genReturnLocal
2805+
// is BAD_VAR_NUM.
2806+
//
2807+
bool Compiler::fgMergeBlockReturn(BasicBlock* block)
2808+
{
2809+
assert(block->KindIs(BBJ_RETURN) && ((block->bbFlags & BBF_HAS_JMP) == 0));
2810+
assert((genReturnBB != nullptr) && (genReturnBB != block));
2811+
2812+
// TODO: Need to characterize the last top level stmt of a block ending with BBJ_RETURN.
2813+
2814+
Statement* lastStmt = block->lastStmt();
2815+
GenTree* ret = (lastStmt != nullptr) ? lastStmt->GetRootNode() : nullptr;
2816+
2817+
if ((ret != nullptr) && (ret->OperGet() == GT_RETURN) && ((ret->gtFlags & GTF_RET_MERGED) != 0))
2818+
{
2819+
// This return was generated during epilog merging, so leave it alone
2820+
return false;
2821+
}
2822+
2823+
#if !defined(TARGET_X86)
2824+
if (info.compFlags & CORINFO_FLG_SYNCH)
2825+
{
2826+
fgConvertSyncReturnToLeave(block);
2827+
return true;
2828+
}
2829+
#endif // !TARGET_X86
2830+
2831+
// We'll jump to the genReturnBB.
2832+
//
2833+
block->SetJumpKindAndTarget(BBJ_ALWAYS, genReturnBB DEBUG_ARG(this));
2834+
fgAddRefPred(genReturnBB, block);
2835+
fgReturnCount--;
2836+
2837+
if (genReturnLocal != BAD_VAR_NUM)
2838+
{
2839+
// replace the GT_RETURN node to be a STORE_LCL_VAR that stores the return value into genReturnLocal.
2840+
2841+
// Method must be returning a value other than TYP_VOID.
2842+
noway_assert(compMethodHasRetVal());
2843+
2844+
// This block must be ending with a GT_RETURN
2845+
noway_assert(lastStmt != nullptr);
2846+
noway_assert(lastStmt->GetNextStmt() == nullptr);
2847+
noway_assert(ret != nullptr);
2848+
2849+
// GT_RETURN must have non-null operand as the method is returning the value assigned to
2850+
// genReturnLocal
2851+
noway_assert(ret->OperGet() == GT_RETURN);
2852+
noway_assert(ret->gtGetOp1() != nullptr);
2853+
2854+
Statement* pAfterStatement = lastStmt;
2855+
const DebugInfo& di = lastStmt->GetDebugInfo();
2856+
GenTree* tree = gtNewTempStore(genReturnLocal, ret->gtGetOp1(), CHECK_SPILL_NONE, &pAfterStatement, di, block);
2857+
2858+
if (pAfterStatement == lastStmt)
2859+
{
2860+
lastStmt->SetRootNode(tree);
2861+
}
2862+
else
2863+
{
2864+
// gtNewTempStore inserted additional statements after last
2865+
fgRemoveStmt(block, lastStmt);
2866+
Statement* newStmt = gtNewStmt(tree, di);
2867+
fgInsertStmtAfter(block, pAfterStatement, newStmt);
2868+
lastStmt = newStmt;
2869+
}
2870+
}
2871+
else if (ret != nullptr && ret->OperGet() == GT_RETURN)
2872+
{
2873+
// This block ends with a GT_RETURN
2874+
noway_assert(lastStmt != nullptr);
2875+
noway_assert(lastStmt->GetNextStmt() == nullptr);
2876+
2877+
// Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn
2878+
// block
2879+
noway_assert(ret->TypeGet() == TYP_VOID);
2880+
noway_assert(ret->gtGetOp1() == nullptr);
2881+
2882+
fgRemoveStmt(block, lastStmt);
2883+
}
2884+
2885+
JITDUMP("\nUpdate " FMT_BB " to jump to common return block.\n", block->bbNum);
2886+
DISPBLOCK(block);
2887+
2888+
if (block->hasProfileWeight())
2889+
{
2890+
weight_t const oldWeight = genReturnBB->hasProfileWeight() ? genReturnBB->bbWeight : BB_ZERO_WEIGHT;
2891+
weight_t const newWeight = oldWeight + block->bbWeight;
2892+
2893+
JITDUMP("merging profile weight " FMT_WT " from " FMT_BB " to common return " FMT_BB "\n", block->bbWeight,
2894+
block->bbNum, genReturnBB->bbNum);
2895+
2896+
genReturnBB->setBBProfileWeight(newWeight);
2897+
DISPBLOCK(genReturnBB);
2898+
}
2899+
2900+
return true;
2901+
}
2902+
27672903
/*****************************************************************************/
27682904
/*****************************************************************************/
27692905

0 commit comments

Comments
 (0)