Skip to content

Commit 5a7aa9d

Browse files
BruceForstalltmds
authored andcommitted
Stop tracking genReturnBB after global morph (dotnet#97130)
* Stop tracking `genReturnBB` after global morph The `genReturnBB` pointer is set if a merged return block is created during `fgAddInternal` (so, quite early during compilation). It needs to be maintained (and the unreferenced block it points to needs to be kept around) until global morph, which hooks up flow to this block. After global morph, clear the pointer and don't maintain it; it is no longer needed. Later code that was checking for it can instead check for `BBJ_RETURN` blocks or `GT_RETURN` nodes. Also, remove the marking of the `genReturnBB` block as `BBF_DONT_REMOVE`. This leads to diffs, as unreachable return blocks get deleted. This can happen, say, if all other exits are tail calls. If that happens and we're in a case of needing a profiler hook, then the profile exit hook is also not needed (it would be unreachable). * Allow x86 synchronized methods to have unreachable return blocks The JIT still needs to report an end-of-synchronized-range offset in the GC info for x86 synchronized methods, so just create a new label at the end of all blocks. The VM will automatically exit the synchronization on an exceptional method exit. * Create final IG before clearing GC info
1 parent 0dda45a commit 5a7aa9d

14 files changed

+67
-61
lines changed

src/coreclr/gcdump/i386/gcdumpx86.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ size_t GCDump::DumpInfoHdr (PTR_CBYTE gcInfoBlock,
154154
gcPrintf(" GuardStack cookie = [%s%u]\n",
155155
header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset);
156156
if (header->syncStartOffset != INVALID_SYNC_OFFSET)
157-
gcPrintf(" Sync region = [%u,%u]\n",
157+
gcPrintf(" Sync region = [%u,%u] ([0x%x,0x%x])\n",
158+
header->syncStartOffset, header->syncEndOffset,
158159
header->syncStartOffset, header->syncEndOffset);
159160

160161
if (header->epilogCount > 1 || (header->epilogCount != 0 &&

src/coreclr/jit/codegencommon.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -7499,6 +7499,7 @@ void CodeGen::genLongReturn(GenTree* treeNode)
74997499
//------------------------------------------------------------------------
75007500
// genReturn: Generates code for return statement.
75017501
// In case of struct return, delegates to the genStructReturn method.
7502+
// In case of LONG return on 32-bit, delegates to the genLongReturn method.
75027503
//
75037504
// Arguments:
75047505
// treeNode - The GT_RETURN or GT_RETFILT tree node.
@@ -7508,7 +7509,8 @@ void CodeGen::genLongReturn(GenTree* treeNode)
75087509
//
75097510
void CodeGen::genReturn(GenTree* treeNode)
75107511
{
7511-
assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
7512+
assert(treeNode->OperIs(GT_RETURN, GT_RETFILT));
7513+
75127514
GenTree* op1 = treeNode->gtGetOp1();
75137515
var_types targetType = treeNode->TypeGet();
75147516

@@ -7609,9 +7611,9 @@ void CodeGen::genReturn(GenTree* treeNode)
76097611
// maintain such an invariant irrespective of whether profiler hook needed or not.
76107612
// Also, there is not much to be gained by materializing it as an explicit node.
76117613
//
7612-
// There should be a single return block while generating profiler ELT callbacks,
7613-
// so we just look for that block to trigger insertion of the profile hook.
7614-
if ((compiler->compCurBB == compiler->genReturnBB) && compiler->compIsProfilerHookNeeded())
7614+
// There should be a single GT_RETURN while generating profiler ELT callbacks.
7615+
//
7616+
if (treeNode->OperIs(GT_RETURN) && compiler->compIsProfilerHookNeeded())
76157617
{
76167618
// !! NOTE !!
76177619
// Since we are invalidating the assumption that we would slip into the epilog

src/coreclr/jit/codegenlinear.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,25 @@ void CodeGen::genCodeForBBlist()
835835
#endif // DEBUG
836836
} //------------------ END-FOR each block of the method -------------------
837837

838+
#if !defined(FEATURE_EH_FUNCLETS)
839+
// If this is a synchronized method on x86, and we generated all the code without
840+
// generating the "exit monitor" call, then we must have deleted the single return block
841+
// with that call because it was dead code. We still need to report the monitor range
842+
// to the VM in the GC info, so create a label at the very end so we have a marker for
843+
// the monitor end range.
844+
//
845+
// Do this before cleaning the GC refs below; we don't want to create an IG that clears
846+
// the `this` pointer for lvaKeepAliveAndReportThis.
847+
848+
if ((compiler->info.compFlags & CORINFO_FLG_SYNCH) && (compiler->syncEndEmitCookie == nullptr))
849+
{
850+
JITDUMP("Synchronized method with missing exit monitor call; adding final label\n");
851+
compiler->syncEndEmitCookie =
852+
GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
853+
noway_assert(compiler->syncEndEmitCookie != nullptr);
854+
}
855+
#endif // !FEATURE_EH_FUNCLETS
856+
838857
// There could be variables alive at this point. For example see lvaKeepAliveAndReportThis.
839858
// This call is for cleaning the GC refs
840859
genUpdateLife(VarSetOps::MakeEmpty(compiler));

src/coreclr/jit/codegenxarch.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -6176,17 +6176,17 @@ void CodeGen::genCall(GenTreeCall* call)
61766176
{
61776177
case CORINFO_HELP_MON_ENTER:
61786178
case CORINFO_HELP_MON_ENTER_STATIC:
6179-
noway_assert(compiler->syncStartEmitCookie == NULL);
6179+
noway_assert(compiler->syncStartEmitCookie == nullptr);
61806180
compiler->syncStartEmitCookie =
61816181
GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
6182-
noway_assert(compiler->syncStartEmitCookie != NULL);
6182+
noway_assert(compiler->syncStartEmitCookie != nullptr);
61836183
break;
61846184
case CORINFO_HELP_MON_EXIT:
61856185
case CORINFO_HELP_MON_EXIT_STATIC:
6186-
noway_assert(compiler->syncEndEmitCookie == NULL);
6186+
noway_assert(compiler->syncEndEmitCookie == nullptr);
61876187
compiler->syncEndEmitCookie =
61886188
GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
6189-
noway_assert(compiler->syncEndEmitCookie != NULL);
6189+
noway_assert(compiler->syncEndEmitCookie != nullptr);
61906190
break;
61916191
default:
61926192
break;

src/coreclr/jit/compiler.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -3332,6 +3332,12 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
33323332
printf("OPTIONS: compProcedureSplitting = %s\n", dspBool(opts.compProcedureSplitting));
33333333
printf("OPTIONS: compProcedureSplittingEH = %s\n", dspBool(opts.compProcedureSplittingEH));
33343334

3335+
// This is rare; don't clutter up the dump with it normally.
3336+
if (compProfilerHookNeeded)
3337+
{
3338+
printf("OPTIONS: compProfilerHookNeeded = %s\n", dspBool(compProfilerHookNeeded));
3339+
}
3340+
33353341
if (jitFlags->IsSet(JitFlags::JIT_FLAG_BBOPT))
33363342
{
33373343
printf("OPTIONS: optimizer should use profile data\n");

src/coreclr/jit/compiler.h

+2-4
Original file line numberDiff line numberDiff line change
@@ -5358,8 +5358,6 @@ class Compiler
53585358
IL_OFFSET fgFindBlockILOffset(BasicBlock* block);
53595359
void fgFixEntryFlowForOSR();
53605360

5361-
void fgUpdateSingleReturnBlock(BasicBlock* block);
5362-
53635361
BasicBlock* fgSplitBlockAtBeginning(BasicBlock* curr);
53645362
BasicBlock* fgSplitBlockAtEnd(BasicBlock* curr);
53655363
BasicBlock* fgSplitBlockAfterStatement(BasicBlock* curr, Statement* stmt);
@@ -10745,14 +10743,14 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1074510743
unsigned compHndBBtabCount; // element count of used elements in EH data array
1074610744
unsigned compHndBBtabAllocCount; // element count of allocated elements in EH data array
1074710745

10748-
#if defined(TARGET_X86)
10746+
#if !defined(FEATURE_EH_FUNCLETS)
1074910747

1075010748
//-------------------------------------------------------------------------
1075110749
// Tracking of region covered by the monitor in synchronized methods
1075210750
void* syncStartEmitCookie; // the emitter cookie for first instruction after the call to MON_ENTER
1075310751
void* syncEndEmitCookie; // the emitter cookie for first instruction after the call to MON_EXIT
1075410752

10755-
#endif // !TARGET_X86
10753+
#endif // !FEATURE_EH_FUNCLETS
1075610754

1075710755
Phases mostRecentlyActivePhase; // the most recently active phase
1075810756
PhaseChecks activePhaseChecks; // the currently active phase checks

src/coreclr/jit/compiler.hpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -2320,7 +2320,7 @@ inline void LclVarDsc::incRefCnts(weight_t weight, Compiler* comp, RefCountState
23202320

23212321
inline bool Compiler::lvaKeepAliveAndReportThis()
23222322
{
2323-
if (info.compIsStatic || lvaTable[0].TypeGet() != TYP_REF)
2323+
if (info.compIsStatic || (lvaTable[0].TypeGet() != TYP_REF))
23242324
{
23252325
return false;
23262326
}
@@ -2354,7 +2354,7 @@ inline bool Compiler::lvaKeepAliveAndReportThis()
23542354
// We keep it alive in the lookup scenario, even when the VM didn't ask us to,
23552355
// because collectible types need the generics context when gc-ing.
23562356
//
2357-
// Methoods that can inspire OSR methods must always report context as live
2357+
// Methods that can inspire OSR methods must always report context as live
23582358
//
23592359
if (genericsContextIsThis)
23602360
{
@@ -4977,8 +4977,9 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
49774977
dfsFrom(fgEntryBB);
49784978
}
49794979

4980-
if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum) && !fgGlobalMorphDone)
4980+
if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
49814981
{
4982+
assert(!fgGlobalMorphDone);
49824983
// We introduce the merged return BB before morph and will redirect
49834984
// other returns to it as part of morph; keep it reachable.
49844985
dfsFrom(genReturnBB);

src/coreclr/jit/emit.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -2333,16 +2333,11 @@ void emitter::emitGeneratePrologEpilog()
23332333

23342334
void emitter::emitStartPrologEpilogGeneration()
23352335
{
2336-
/* Save the current IG if it's non-empty */
2337-
2338-
if (emitCurIGnonEmpty())
2336+
// Save the current IG if we have one. It might be empty if we added an end-of-compilation label.
2337+
if (emitCurIG != nullptr)
23392338
{
23402339
emitSavIG();
23412340
}
2342-
else
2343-
{
2344-
assert(emitCurIG == nullptr);
2345-
}
23462341
}
23472342

23482343
/*****************************************************************************

src/coreclr/jit/fgbasic.cpp

-20
Original file line numberDiff line numberDiff line change
@@ -4755,24 +4755,6 @@ IL_OFFSET Compiler::fgFindBlockILOffset(BasicBlock* block)
47554755
return BAD_IL_OFFSET;
47564756
}
47574757

4758-
//------------------------------------------------------------------------------
4759-
// fgUpdateSingleReturnBlock : A block has been split. If it was the single return
4760-
// block, then update the single return block pointer.
4761-
//
4762-
// Arguments:
4763-
// block - The block that was split
4764-
//
4765-
void Compiler::fgUpdateSingleReturnBlock(BasicBlock* block)
4766-
{
4767-
assert(block->KindIs(BBJ_ALWAYS));
4768-
if (genReturnBB == block)
4769-
{
4770-
assert(block->GetTarget()->KindIs(BBJ_RETURN));
4771-
JITDUMP("Updating genReturnBB from " FMT_BB " to " FMT_BB "\n", block->bbNum, block->GetTarget()->bbNum);
4772-
genReturnBB = block->GetTarget();
4773-
}
4774-
}
4775-
47764758
//------------------------------------------------------------------------------
47774759
// fgSplitBlockAtEnd - split the given block into two blocks.
47784760
// All code in the block stays in the original block.
@@ -4849,8 +4831,6 @@ BasicBlock* Compiler::fgSplitBlockAtEnd(BasicBlock* curr)
48494831

48504832
fgAddRefPred(newBlock, curr);
48514833

4852-
fgUpdateSingleReturnBlock(curr);
4853-
48544834
return newBlock;
48554835
}
48564836

src/coreclr/jit/fgdiagnostic.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -2255,10 +2255,10 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 *
22552255
}
22562256
}
22572257

2258-
// Indicate if it's the single return block
2258+
// Indicate if it's the merged return block.
22592259
if (block == genReturnBB)
22602260
{
2261-
printf(" one-return");
2261+
printf(" merged-return");
22622262
}
22632263

22642264
printf("\n");

src/coreclr/jit/flowgraph.cpp

+7-12
Original file line numberDiff line numberDiff line change
@@ -1868,7 +1868,7 @@ class MergedReturns
18681868
return;
18691869
}
18701870

1871-
// We'e reached our threshold
1871+
// We've reached our threshold
18721872
mergingReturns = true;
18731873

18741874
// Merge any returns we've already identified
@@ -2478,29 +2478,25 @@ PhaseStatus Compiler::fgAddInternal()
24782478

24792479
if (info.compFlags & CORINFO_FLG_SYNCH)
24802480
{
2481-
GenTree* tree = NULL;
2481+
GenTree* tree = nullptr;
24822482

24832483
/* Insert the expression "enterCrit(this)" or "enterCrit(handle)" */
24842484

24852485
if (info.compIsStatic)
24862486
{
24872487
tree = fgGetCritSectOfStaticMethod();
2488-
24892488
tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, tree);
24902489
}
24912490
else
24922491
{
24932492
noway_assert(lvaTable[info.compThisArg].lvType == TYP_REF);
2494-
24952493
tree = gtNewLclvNode(info.compThisArg, TYP_REF);
2496-
24972494
tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, tree);
24982495
}
24992496

25002497
/* Create a new basic block and stick the call in it */
25012498

25022499
fgEnsureFirstBBisScratch();
2503-
25042500
fgNewStmtAtEnd(fgFirstBB, tree);
25052501

25062502
#ifdef DEBUG
@@ -2522,13 +2518,11 @@ PhaseStatus Compiler::fgAddInternal()
25222518
if (info.compIsStatic)
25232519
{
25242520
tree = fgGetCritSectOfStaticMethod();
2525-
25262521
tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, tree);
25272522
}
25282523
else
25292524
{
25302525
tree = gtNewLclvNode(info.compThisArg, TYP_REF);
2531-
25322526
tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, tree);
25332527
}
25342528

@@ -2537,15 +2531,16 @@ PhaseStatus Compiler::fgAddInternal()
25372531
#ifdef DEBUG
25382532
if (verbose)
25392533
{
2540-
printf("\nSynchronized method - Add exit expression ");
2541-
printTreeID(tree);
2534+
printf("\nSynchronized method - Add exitCrit statement in single return block %s\n",
2535+
genReturnBB->dspToString());
2536+
gtDispTree(tree);
25422537
printf("\n");
25432538
}
25442539
#endif
25452540

25462541
// Reset cookies used to track start and end of the protected region in synchronized methods
2547-
syncStartEmitCookie = NULL;
2548-
syncEndEmitCookie = NULL;
2542+
syncStartEmitCookie = nullptr;
2543+
syncEndEmitCookie = nullptr;
25492544
madeChanges = true;
25502545
}
25512546

src/coreclr/jit/gcencode.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -1563,15 +1563,16 @@ size_t GCInfo::gcInfoBlockHdrSave(
15631563

15641564
header->syncStartOffset = INVALID_SYNC_OFFSET;
15651565
header->syncEndOffset = INVALID_SYNC_OFFSET;
1566+
15661567
#ifndef UNIX_X86_ABI
15671568
// JIT is responsible for synchronization on funclet-based EH model that x86/Linux uses.
15681569
if (compiler->info.compFlags & CORINFO_FLG_SYNCH)
15691570
{
1570-
assert(compiler->syncStartEmitCookie != NULL);
1571+
assert(compiler->syncStartEmitCookie != nullptr);
15711572
header->syncStartOffset = compiler->GetEmitter()->emitCodeOffset(compiler->syncStartEmitCookie, 0);
15721573
assert(header->syncStartOffset != INVALID_SYNC_OFFSET);
15731574

1574-
assert(compiler->syncEndEmitCookie != NULL);
1575+
assert(compiler->syncEndEmitCookie != nullptr);
15751576
header->syncEndOffset = compiler->GetEmitter()->emitCodeOffset(compiler->syncEndEmitCookie, 0);
15761577
assert(header->syncEndOffset != INVALID_SYNC_OFFSET);
15771578

src/coreclr/jit/lower.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -4271,7 +4271,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret)
42714271
}
42724272

42734273
// Method doing PInvokes has exactly one return block unless it has tail calls.
4274-
if (comp->compMethodRequiresPInvokeFrame() && (comp->compCurBB == comp->genReturnBB))
4274+
if (comp->compMethodRequiresPInvokeFrame())
42754275
{
42764276
InsertPInvokeMethodEpilog(comp->compCurBB DEBUGARG(ret));
42774277
}
@@ -5369,7 +5369,7 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTree*
53695369
JITDUMP("======= Inserting PInvoke method epilog\n");
53705370

53715371
// Method doing PInvoke calls has exactly one return block unless it has "jmp" or tail calls.
5372-
assert(((returnBB == comp->genReturnBB) && returnBB->KindIs(BBJ_RETURN)) || returnBB->endsWithTailCallOrJmp(comp));
5372+
assert(returnBB->KindIs(BBJ_RETURN) || returnBB->endsWithTailCallOrJmp(comp));
53735373

53745374
LIR::Range& returnBlockRange = LIR::AsRange(returnBB);
53755375

src/coreclr/jit/morph.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -14140,6 +14140,14 @@ PhaseStatus Compiler::fgMorphBlocks()
1414014140
fgEntryBB = nullptr;
1414114141
}
1414214142

14143+
// We don't maintain `genReturnBB` after this point.
14144+
if (genReturnBB != nullptr)
14145+
{
14146+
// It no longer needs special "keep" treatment.
14147+
genReturnBB->RemoveFlags(BBF_DONT_REMOVE);
14148+
genReturnBB = nullptr;
14149+
}
14150+
1414314151
// We are done with the global morphing phase
1414414152
//
1414514153
fgInvalidateDfsTree();

0 commit comments

Comments
 (0)