Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

JIT: add some devirtualization info to the inline context #20395

Merged
merged 2 commits into from
Oct 13, 2018
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
7 changes: 4 additions & 3 deletions src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21878,8 +21878,9 @@ void Compiler::fgInline()

if (verbose || fgPrintInlinedMethods)
{
printf("**************** Inline Tree\n");
m_inlineStrategy->Dump();
JITDUMP("**************** Inline Tree");
printf("\n");
sandreenko marked this conversation as resolved.
Show resolved Hide resolved
m_inlineStrategy->Dump(verbose);
}

#endif // DEBUG
Expand Down Expand Up @@ -22573,7 +22574,7 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe

#ifdef DEBUG

if (verbose || fgPrintInlinedMethods)
if (verbose)
{
printf("Successfully inlined %s (%d IL bytes) (depth %d) [%s]\n", eeGetMethodFullName(fncHandle),
inlineCandidateInfo->methInfo.ILCodeSize, inlineDepth, inlineResult->ReasonString());
Expand Down
12 changes: 12 additions & 0 deletions src/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -3537,6 +3537,8 @@ struct GenTreeCall final : public GenTree
// stubs, because executable code cannot be generated at runtime.
#define GTF_CALL_M_HELPER_SPECIAL_DCE 0x00020000 // GT_CALL -- this helper call can be removed if it is part of a comma and
// the comma result is unused.
#define GTF_CALL_M_DEVIRTUALIZED 0x00040000 // GT_CALL -- this call was devirtualized
#define GTF_CALL_M_UNBOXED 0x00080000 // GT_CALL -- this call was optimized to use the unboxed entry point

// clang-format on

Expand Down Expand Up @@ -3743,6 +3745,16 @@ struct GenTreeCall final : public GenTree
gtCallMoreFlags |= GTF_CALL_M_FAT_POINTER_CHECK;
}

bool IsDevirtualized() const
{
return (gtCallMoreFlags & GTF_CALL_M_DEVIRTUALIZED) != 0;
}

bool IsUnboxed() const
{
return (gtCallMoreFlags & GTF_CALL_M_UNBOXED) != 0;
}

unsigned gtCallMoreFlags; // in addition to gtFlags

unsigned char gtCallType : 3; // value from the gtCallTypes enumeration
Expand Down
5 changes: 4 additions & 1 deletion src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19631,6 +19631,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
call->gtFlags &= ~GTF_CALL_VIRT_STUB;
call->gtCallMethHnd = derivedMethod;
call->gtCallType = CT_USER_FUNC;
call->gtCallMoreFlags |= GTF_CALL_M_DEVIRTUALIZED;

// Virtual calls include an implicit null check, which we may
// now need to make explicit.
Expand Down Expand Up @@ -19695,6 +19696,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
// Pass the local var as this and the type handle as a new arg
JITDUMP("Success! invoking unboxed entry point on local copy, and passing method table arg\n");
call->gtCallObjp = localCopyThis;
call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;

// Prepend for R2L arg passing or empty L2R passing
if ((Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) || (call->gtCallArgs == nullptr))
Expand Down Expand Up @@ -19742,7 +19744,8 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
JITDUMP("Success! invoking unboxed entry point on local copy\n");
call->gtCallObjp = localCopyThis;
call->gtCallMethHnd = unboxedEntryMethod;
derivedMethod = unboxedEntryMethod;
call->gtCallMoreFlags |= GTF_CALL_M_UNBOXED;
derivedMethod = unboxedEntryMethod;
}
else
{
Expand Down
72 changes: 44 additions & 28 deletions src/jit/inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ InlineContext::InlineContext(InlineStrategy* strategy)
, m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
, m_CodeSizeEstimate(0)
, m_Success(true)
, m_Devirtualized(false)
, m_Unboxed(false)
#if defined(DEBUG) || defined(INLINE_DATA)
, m_Policy(nullptr)
, m_Callee(nullptr)
Expand Down Expand Up @@ -392,19 +394,21 @@ void InlineContext::Dump(unsigned indent)
else
{
// Inline attempt.
const char* inlineReason = InlGetObservationString(m_Observation);
const char* inlineResult = m_Success ? "" : "FAILED: ";
const char* inlineReason = InlGetObservationString(m_Observation);
const char* inlineResult = m_Success ? "" : "FAILED: ";
const char* devirtualized = m_Devirtualized ? " devirt" : "";
const char* unboxed = m_Unboxed ? " unboxed" : "";

if (m_Offset == BAD_IL_OFFSET)
{
printf("%*s[%u IL=???? TR=%06u %08X] [%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken,
inlineResult, inlineReason, calleeName);
printf("%*s[%u IL=???? TR=%06u %08X] [%s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken,
inlineResult, inlineReason, devirtualized, unboxed, calleeName);
}
else
{
IL_OFFSET offset = jitGetILoffs(m_Offset);
printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID, calleeToken,
inlineResult, inlineReason, calleeName);
printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID, calleeToken,
inlineResult, inlineReason, devirtualized, unboxed, calleeName);
}
}

Expand Down Expand Up @@ -1172,7 +1176,6 @@ InlineContext* InlineStrategy::NewRoot()
// and link it into the context tree
//
// Arguments:
// stmt - statement containing call being inlined
// inlineInfo - information about this inline
//
// Return Value:
Expand All @@ -1186,6 +1189,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize;
InlineContext* parentContext = stmt->gtInlineContext;
GenTreeCall* originalCall = inlineInfo->inlineResult->GetCall();

noway_assert(parentContext != nullptr);

Expand All @@ -1194,12 +1198,14 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
calleeContext->m_Parent = parentContext;
// Push on front here will put siblings in reverse lexical
// order which we undo in the dumper
calleeContext->m_Sibling = parentContext->m_Child;
parentContext->m_Child = calleeContext;
calleeContext->m_Child = nullptr;
calleeContext->m_Offset = stmt->AsStmt()->gtStmtILoffsx;
calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation();
calleeContext->m_Success = true;
calleeContext->m_Sibling = parentContext->m_Child;
parentContext->m_Child = calleeContext;
calleeContext->m_Child = nullptr;
calleeContext->m_Offset = stmt->gtStmtILoffsx;
calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation();
calleeContext->m_Success = true;
calleeContext->m_Devirtualized = originalCall->IsDevirtualized();
calleeContext->m_Unboxed = originalCall->IsUnboxed();

#if defined(DEBUG) || defined(INLINE_DATA)

Expand All @@ -1211,13 +1217,13 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
// +1 here since we set this before calling NoteOutcome.
calleeContext->m_Ordinal = m_InlineCount + 1;
// Update offset with more accurate info
calleeContext->m_Offset = inlineInfo->inlineResult->GetCall()->gtRawILOffset;
calleeContext->m_Offset = originalCall->gtRawILOffset;

#endif // defined(DEBUG) || defined(INLINE_DATA)

#if defined(DEBUG)

calleeContext->m_TreeID = inlineInfo->inlineResult->GetCall()->gtTreeID;
calleeContext->m_TreeID = originalCall->gtTreeID;

#endif // defined(DEBUG)

Expand All @@ -1237,8 +1243,7 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
// inlineResult - inlineResult for the attempt
//
// Return Value:
// A new InlineContext for diagnostic purposes, or nullptr if
// the desired context could not be created.
// A new InlineContext for diagnostic purposes

InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlineResult)
{
Expand All @@ -1247,31 +1252,34 @@ InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlin
InlineContext* parentContext = stmt->gtInlineContext;
assert(parentContext != nullptr);
InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
GenTreeCall* originalCall = inlineResult->GetCall();

// Pushing the new context on the front of the parent child list
// will put siblings in reverse lexical order which we undo in the
// dumper.
failedContext->m_Parent = parentContext;
failedContext->m_Sibling = parentContext->m_Child;
parentContext->m_Child = failedContext;
failedContext->m_Child = nullptr;
failedContext->m_Offset = stmt->gtStmtILoffsx;
failedContext->m_Observation = inlineResult->GetObservation();
failedContext->m_Callee = inlineResult->GetCallee();
failedContext->m_Success = false;
failedContext->m_Parent = parentContext;
failedContext->m_Sibling = parentContext->m_Child;
parentContext->m_Child = failedContext;
failedContext->m_Child = nullptr;
failedContext->m_Offset = stmt->gtStmtILoffsx;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you are here, could you please fix headers/signature for
InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
and
InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlineResult),
they probably should be the same (and the headers are), but NewSuccess doesn't have stmt - statement containing call being inlined as argument.

Then maybe it will be obvious how to extract this two similar initialization lists into one method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update the comment, but are you suggesting common out some of the work these two methods do?

It may be clunky as they don't have access to the same data.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can delete GenTreeStmt* stmt arg from NewFailure and do what NewSuccess does:
GenTreeStmt* stmt = inlineInfo->iciStmt;
calleeContext->m_Offset = stmt->AsStmt()->gtStmtILoffsx; <- looks like we do not need this cast here, it is already a statement.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently don't have access to the InlineInfo for the failure cases.

I'll remove the cast too.

failedContext->m_Observation = inlineResult->GetObservation();
failedContext->m_Callee = inlineResult->GetCallee();
failedContext->m_Success = false;
failedContext->m_Devirtualized = originalCall->IsDevirtualized();
failedContext->m_Unboxed = originalCall->IsUnboxed();

assert(InlIsValidObservation(failedContext->m_Observation));

#if defined(DEBUG) || defined(INLINE_DATA)

// Update offset with more accurate info
failedContext->m_Offset = inlineResult->GetCall()->gtRawILOffset;
failedContext->m_Offset = originalCall->gtRawILOffset;

#endif // #if defined(DEBUG) || defined(INLINE_DATA)

#if defined(DEBUG)

failedContext->m_TreeID = inlineResult->GetCall()->gtTreeID;
failedContext->m_TreeID = originalCall->gtTreeID;

#endif // defined(DEBUG)

Expand All @@ -1282,11 +1290,19 @@ InlineContext* InlineStrategy::NewFailure(GenTreeStmt* stmt, InlineResult* inlin

//------------------------------------------------------------------------
// Dump: dump description of inline behavior
//
// Arguments:
// showBudget - also dump final budget values

void InlineStrategy::Dump()
void InlineStrategy::Dump(bool showBudget)
{
m_RootContext->Dump();

if (!showBudget)
{
return;
}

printf("Budget: initialTime=%d, finalTime=%d, initialBudget=%d, currentBudget=%d\n", m_InitialTimeEstimate,
m_CurrentTimeEstimate, m_InitialTimeBudget, m_CurrentTimeBudget);

Expand Down
34 changes: 23 additions & 11 deletions src/jit/inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,20 +677,32 @@ class InlineContext
return m_Parent == nullptr;
}

bool IsDevirtualized() const
{
return m_Devirtualized;
}

bool IsUnboxed() const
{
return m_Unboxed;
}

private:
InlineContext(InlineStrategy* strategy);

private:
InlineStrategy* m_InlineStrategy; // overall strategy
InlineContext* m_Parent; // logical caller (parent)
InlineContext* m_Child; // first child
InlineContext* m_Sibling; // next child of the parent
BYTE* m_Code; // address of IL buffer for the method
unsigned m_ILSize; // size of IL buffer for the method
IL_OFFSETX m_Offset; // call site location within parent
InlineObservation m_Observation; // what lead to this inline
int m_CodeSizeEstimate; // in bytes * 10
bool m_Success; // true if this was a successful inline
InlineStrategy* m_InlineStrategy; // overall strategy
InlineContext* m_Parent; // logical caller (parent)
InlineContext* m_Child; // first child
InlineContext* m_Sibling; // next child of the parent
BYTE* m_Code; // address of IL buffer for the method
unsigned m_ILSize; // size of IL buffer for the method
IL_OFFSETX m_Offset; // call site location within parent
InlineObservation m_Observation; // what lead to this inline
int m_CodeSizeEstimate; // in bytes * 10
bool m_Success : 1; // true if this was a successful inline
bool m_Devirtualized : 1; // true if this was a devirtualized call
bool m_Unboxed : 1; // true if this call now invokes the unboxed entry

#if defined(DEBUG) || defined(INLINE_DATA)

Expand Down Expand Up @@ -817,7 +829,7 @@ class InlineStrategy
#if defined(DEBUG) || defined(INLINE_DATA)

// Dump textual description of inlines done so far.
void Dump();
void Dump(bool showBudget);

// Dump data-format description of inlines done so far.
void DumpData();
Expand Down