Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: rework logic for when OSR imports method entry #63406

Merged
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
14 changes: 14 additions & 0 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2145,6 +2145,20 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 *

block->dspFlags();

// Display OSR info
//
if (opts.IsOSR())
{
if (block == fgEntryBB)
{
printf("original-entry");
}
if (block == fgOSREntryBB)
{
printf("osr-entry");
}
}

printf("\n");
}

Expand Down
126 changes: 80 additions & 46 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9766,52 +9766,6 @@ var_types Compiler::impImportCall(OPCODE opcode,
}
}

// A tail recursive call is a potential loop from the current block to the start of the method.
if ((tailCallFlags != 0) && canTailCall)
{
// If a root method tail call candidate block is not a BBJ_RETURN, it should have a unique
// BBJ_RETURN successor. Mark that successor so we can handle it specially during profile
// instrumentation.
//
if (!compIsForInlining() && (compCurBB->bbJumpKind != BBJ_RETURN))
{
BasicBlock* const successor = compCurBB->GetUniqueSucc();
assert(successor->bbJumpKind == BBJ_RETURN);
successor->bbFlags |= BBF_TAILCALL_SUCCESSOR;
optMethodFlags |= OMF_HAS_TAILCALL_SUCCESSOR;
}

if (gtIsRecursiveCall(methHnd))
{
assert(verCurrentState.esStackDepth == 0);
BasicBlock* loopHead = nullptr;
if (opts.IsOSR())
{
// We might not have been planning on importing the method
// entry block, but now we must.

// We should have remembered the real method entry block.
assert(fgEntryBB != nullptr);

JITDUMP("\nOSR: found tail recursive call in the method, scheduling " FMT_BB " for importation\n",
fgEntryBB->bbNum);
impImportBlockPending(fgEntryBB);
loopHead = fgEntryBB;
}
else
{
// For normal jitting we'll branch back to the firstBB; this
// should already be imported.
loopHead = fgFirstBB;
}

JITDUMP("\nFound tail recursive call in the method. Mark " FMT_BB " to " FMT_BB
" as having a backward branch.\n",
loopHead->bbNum, compCurBB->bbNum);
fgMarkBackwardJump(loopHead, compCurBB);
}
}

// Note: we assume that small return types are already normalized by the managed callee
// or by the pinvoke stub for calls to unmanaged code.

Expand Down Expand Up @@ -9847,6 +9801,86 @@ var_types Compiler::impImportCall(OPCODE opcode,
impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo);
}

// Extra checks for tail calls and tail recursion.
//
// A tail recursive call is a potential loop from the current block to the start of the root method.
// If we see a tail recursive call, mark the blocks from the call site back to the entry as potentially
// being in a loop.
//
// Note: if we're importing an inlinee we don't mark the right set of blocks, but by then it's too
// late. Currently this doesn't lead to problems. See GitHub issue 33529.
//
// OSR also needs to handle tail calls specially:
// * block profiling in OSR methods needs to ensure probes happen before tail calls, not after.
// * the root method entry must be imported if there's a recursive tail call or a potentially
// inlineable tail call.
//
if ((tailCallFlags != 0) && canTailCall)
{
if (gtIsRecursiveCall(methHnd))
{
assert(verCurrentState.esStackDepth == 0);
BasicBlock* loopHead = nullptr;
if (!compIsForInlining() && opts.IsOSR())
{
// For root method OSR we may branch back to the actual method entry,
// which is not fgFirstBB, and which we will need to import.
assert(fgEntryBB != nullptr);
loopHead = fgEntryBB;
}
else
{
// For normal jitting we may branch back to the firstBB; this
// should already be imported.
loopHead = fgFirstBB;
}

JITDUMP("\nTail recursive call [%06u] in the method. Mark " FMT_BB " to " FMT_BB
" as having a backward branch.\n",
dspTreeID(call), loopHead->bbNum, compCurBB->bbNum);
fgMarkBackwardJump(loopHead, compCurBB);
}

// We only do these OSR checks in the root method because:
// * If we fail to import the root method entry when importing the root method, we can't go back
// and import it during inlining. So instead of checking jsut for recursive tail calls we also
// have to check for anything that might introduce a recursive tail call.
// * We only instrument root method blocks in OSR methods,
//
if (opts.IsOSR() && !compIsForInlining())
{
// If a root method tail call candidate block is not a BBJ_RETURN, it should have a unique
// BBJ_RETURN successor. Mark that successor so we can handle it specially during profile
// instrumentation.
//
if (compCurBB->bbJumpKind != BBJ_RETURN)
{
BasicBlock* const successor = compCurBB->GetUniqueSucc();
assert(successor->bbJumpKind == BBJ_RETURN);
successor->bbFlags |= BBF_TAILCALL_SUCCESSOR;
optMethodFlags |= OMF_HAS_TAILCALL_SUCCESSOR;
}

// If this call might eventually turn into a loop back to method entry, make sure we
// import the method entry.
//
assert(call->IsCall());
GenTreeCall* const actualCall = call->AsCall();
const bool mustImportEntryBlock = gtIsRecursiveCall(methHnd) || actualCall->IsInlineCandidate() ||
actualCall->IsGuardedDevirtualizationCandidate();

// Only schedule importation if we're not currently importing.
//
if (mustImportEntryBlock && (compCurBB != fgEntryBB))
{
JITDUMP("\nOSR: inlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB
" for importation\n",
dspTreeID(call), fgEntryBB->bbNum);
impImportBlockPending(fgEntryBB);
}
}
}

if ((sig->flags & CORINFO_SIGFLAG_FAT_CALL) != 0)
{
assert(opcode == CEE_CALLI || callInfo->kind == CORINFO_CALL_CODE_POINTER);
Expand Down