Skip to content

Commit

Permalink
Allow x86 synchronized methods to have unreachable return blocks
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
BruceForstall committed Jan 20, 2024
1 parent 9d244a7 commit d01f784
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 27 deletions.
3 changes: 2 additions & 1 deletion src/coreclr/gcdump/i386/gcdumpx86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ size_t GCDump::DumpInfoHdr (PTR_CBYTE gcInfoBlock,
gcPrintf(" GuardStack cookie = [%s%u]\n",
header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset);
if (header->syncStartOffset != INVALID_SYNC_OFFSET)
gcPrintf(" Sync region = [%u,%u]\n",
gcPrintf(" Sync region = [%u,%u] ([0x%x,0x%x])\n",
header->syncStartOffset, header->syncEndOffset,
header->syncStartOffset, header->syncEndOffset);

if (header->epilogCount > 1 || (header->epilogCount != 0 &&
Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,24 @@ void CodeGen::genCodeForBBlist()
// This call is for cleaning the GC refs
genUpdateLife(VarSetOps::MakeEmpty(compiler));

#if !defined(FEATURE_EH_FUNCLETS)
// If this is a synchronized method on x86, and we generated all the code without
// generating the "exit monitor" call, then we must have deleted the single return block
// with that call because it was dead code. We still need to report the monitor range
// to the VM in the GC info, so create a label at the very end so we have a marker for
// the monitor end range.

if ((compiler->info.compFlags & CORINFO_FLG_SYNCH) && (compiler->syncEndEmitCookie == nullptr))
{
JITDUMP("Synchronized method with missing exit monitor call; adding final label\n");

// The GC liveness sets should all be empty.
compiler->syncEndEmitCookie =
GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
noway_assert(compiler->syncEndEmitCookie != nullptr);
}
#endif // !FEATURE_EH_FUNCLETS

/* Finalize the spill tracking logic */

regSet.rsSpillEnd();
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6176,17 +6176,17 @@ void CodeGen::genCall(GenTreeCall* call)
{
case CORINFO_HELP_MON_ENTER:
case CORINFO_HELP_MON_ENTER_STATIC:
noway_assert(compiler->syncStartEmitCookie == NULL);
noway_assert(compiler->syncStartEmitCookie == nullptr);
compiler->syncStartEmitCookie =
GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
noway_assert(compiler->syncStartEmitCookie != NULL);
noway_assert(compiler->syncStartEmitCookie != nullptr);
break;
case CORINFO_HELP_MON_EXIT:
case CORINFO_HELP_MON_EXIT_STATIC:
noway_assert(compiler->syncEndEmitCookie == NULL);
noway_assert(compiler->syncEndEmitCookie == nullptr);
compiler->syncEndEmitCookie =
GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
noway_assert(compiler->syncEndEmitCookie != NULL);
noway_assert(compiler->syncEndEmitCookie != nullptr);
break;
default:
break;
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -10743,14 +10743,14 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
unsigned compHndBBtabCount; // element count of used elements in EH data array
unsigned compHndBBtabAllocCount; // element count of allocated elements in EH data array

#if defined(TARGET_X86)
#if !defined(FEATURE_EH_FUNCLETS)

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

#endif // !TARGET_X86
#endif // !FEATURE_EH_FUNCLETS

Phases mostRecentlyActivePhase; // the most recently active phase
PhaseChecks activePhaseChecks; // the currently active phase checks
Expand Down
9 changes: 2 additions & 7 deletions src/coreclr/jit/emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2333,16 +2333,11 @@ void emitter::emitGeneratePrologEpilog()

void emitter::emitStartPrologEpilogGeneration()
{
/* Save the current IG if it's non-empty */

if (emitCurIGnonEmpty())
// Save the current IG if we have one. It might be empty if we added an end-of-compilation label.
if (emitCurIG != nullptr)
{
emitSavIG();
}
else
{
assert(emitCurIG == nullptr);
}
}

/*****************************************************************************
Expand Down
17 changes: 6 additions & 11 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2478,29 +2478,25 @@ PhaseStatus Compiler::fgAddInternal()

if (info.compFlags & CORINFO_FLG_SYNCH)
{
GenTree* tree = NULL;
GenTree* tree = nullptr;

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

if (info.compIsStatic)
{
tree = fgGetCritSectOfStaticMethod();

tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, tree);
}
else
{
noway_assert(lvaTable[info.compThisArg].lvType == TYP_REF);

tree = gtNewLclvNode(info.compThisArg, TYP_REF);

tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, tree);
}

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

fgEnsureFirstBBisScratch();

fgNewStmtAtEnd(fgFirstBB, tree);

#ifdef DEBUG
Expand All @@ -2522,13 +2518,11 @@ PhaseStatus Compiler::fgAddInternal()
if (info.compIsStatic)
{
tree = fgGetCritSectOfStaticMethod();

tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, tree);
}
else
{
tree = gtNewLclvNode(info.compThisArg, TYP_REF);

tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, tree);
}

Expand All @@ -2537,15 +2531,16 @@ PhaseStatus Compiler::fgAddInternal()
#ifdef DEBUG
if (verbose)
{
printf("\nSynchronized method - Add exit expression ");
printTreeID(tree);
printf("\nSynchronized method - Add exitCrit statement in single return block %s\n",
genReturnBB->dspToString());
gtDispTree(tree);
printf("\n");
}
#endif

// Reset cookies used to track start and end of the protected region in synchronized methods
syncStartEmitCookie = NULL;
syncEndEmitCookie = NULL;
syncStartEmitCookie = nullptr;
syncEndEmitCookie = nullptr;
madeChanges = true;
}

Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/jit/gcencode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1563,15 +1563,16 @@ size_t GCInfo::gcInfoBlockHdrSave(

header->syncStartOffset = INVALID_SYNC_OFFSET;
header->syncEndOffset = INVALID_SYNC_OFFSET;

#ifndef UNIX_X86_ABI
// JIT is responsible for synchronization on funclet-based EH model that x86/Linux uses.
if (compiler->info.compFlags & CORINFO_FLG_SYNCH)
{
assert(compiler->syncStartEmitCookie != NULL);
assert(compiler->syncStartEmitCookie != nullptr);
header->syncStartOffset = compiler->GetEmitter()->emitCodeOffset(compiler->syncStartEmitCookie, 0);
assert(header->syncStartOffset != INVALID_SYNC_OFFSET);

assert(compiler->syncEndEmitCookie != NULL);
assert(compiler->syncEndEmitCookie != nullptr);
header->syncEndOffset = compiler->GetEmitter()->emitCodeOffset(compiler->syncEndEmitCookie, 0);
assert(header->syncEndOffset != INVALID_SYNC_OFFSET);

Expand Down

0 comments on commit d01f784

Please sign in to comment.