return ptr->bbNum;
}
-//------------------------------------------------------------------------
-// inlExpLst: default constructor for an inlExpList
-//
-// Notes: use for the root instance. We set ixlCode to nullptr here
-// (rather than the IL buffer address of the root method) to preserve
-// existing behavior, which is to allow one recursive inline.
-
-inlExpLst::inlExpLst() : ixlParent(nullptr), ixlChild(nullptr),
- ixlSibling(nullptr), ilOffset(BAD_IL_OFFSET),
- ixlCode(nullptr)
-{
-#ifdef DEBUG
- methodName = nullptr;
- depth = 0;
-#endif
-}
-
-
-#ifdef DEBUG
-
-//------------------------------------------------------------------------
-// Dump: Dump an inlExpLst entry and all descendants to stdout
-//
-// Arguments:
-// indent: indentation level for this node
-
-void inlExpLst::Dump(int indent)
-{
- // Handle fact that siblings are in reverse order.
- if (ixlSibling != nullptr)
- {
- ixlSibling->Dump(indent);
- }
-
- // Dump this node
- if (ixlParent == nullptr)
- {
- // root
- printf("Inlines into %s\n", methodName);
- }
- else
- {
- for (int i = 0; i < indent; i++)
- {
- printf(" ");
- }
-
- if (ilOffset == BAD_IL_OFFSET)
- {
- printf("[IL=?] %s\n", methodName);
- }
- else
- {
- IL_OFFSET offset = jitGetILoffs(ilOffset);
- printf("[IL=%d] %s\n", offset, methodName);
- }
- }
-
- // Recurse to first child
- if (ixlChild != nullptr)
- {
- ixlChild->Dump(indent + 2);
- }
-}
-#endif
StackEntry* esStack; // ptr to stack
};
-
-//-----------------------------------------------------------------------------
-//
-// The following keeps track of the currently expanded inline functions.
-// Any method currently on the list should not be inlined since that
-// implies that it's being called recursively.
-// We track the IL code body so we don't get confused by generics.
-//
-
-struct inlExpLst
-{
- // Default constructor, suitable for root instance
- inlExpLst();
-
- inlExpLst* ixlParent; // logical caller (parent)
- inlExpLst* ixlChild; // first child
- inlExpLst* ixlSibling; // next child of the parent
- IL_OFFSETX ilOffset; // call site location within parent
- BYTE* ixlCode; // address of IL buffer for the method
-
-#ifdef DEBUG
- const char * methodName;
- unsigned depth;
-
- // Dump this entry and all descendants
- void Dump(int indent = 0);
-#endif
-};
-
-typedef inlExpLst* inlExpPtr;
-
// This encapsulates the "exception handling" successors of a block. That is,
// if a basic block BB1 occurs in a try block, we consider the first basic block
// BB2 of the corresponding handler to be an "EH successor" of BB1. Because we
unsigned fgBigOffsetMorphingTemps[TYP_COUNT];
- static bool fgIsUnboundedInlineRecursion(inlExpPtr expLst,
- BYTE * ilCode,
- DWORD* depth);
+ static bool fgIsUnboundedInlineRecursion(InlineContext* context,
+ BYTE* ilCode,
+ DWORD* depth);
void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result);
void fgInsertInlineeBlocks (InlineInfo * pInlineInfo);
* Also return the depth.
*/
-bool Compiler::fgIsUnboundedInlineRecursion(inlExpPtr expLst,
+bool Compiler::fgIsUnboundedInlineRecursion(InlineContext* inlineContext,
BYTE* ilCode,
DWORD* finalDepth)
{
DWORD depth = 0;
bool result = false;
- for (; expLst != nullptr; expLst = expLst->ixlParent)
+ for (; inlineContext != nullptr; inlineContext = inlineContext->inlParent)
{
// Hard limit just to catch pathological cases
depth++;
- if ((expLst->ixlCode == ilCode) || (depth > MAX_INLINING_RECURSION_DEPTH))
+ if ((inlineContext->inlCode == ilCode) || (depth > MAX_INLINING_RECURSION_DEPTH))
{
result = true;
break;
noway_assert(block != nullptr);
// Set the root inline context on all statements
- inlExpLst* expDsc = new (this, CMK_Inlining) inlExpLst();
+ InlineContext* rootContext = new (this, CMK_Inlining) InlineContext;
#if defined(DEBUG)
- expDsc->methodName = info.compFullName;
+ rootContext->inlCallee = info.compMethodHnd;
#endif
for (; block != nullptr; block = block->bbNext)
stmt;
stmt = stmt->gtNextStmt)
{
- stmt->gtInlineExpList = expDsc;
+ stmt->gtInlineContext = rootContext;
}
}
if (verbose || (fgInlinedCount > 0 && fgPrintInlinedMethods))
{
printf("**************** Inline Tree\n");
- expDsc->Dump();
+ rootContext->Dump(this);
}
#endif // DEBUG
// Store the link to inlineCandidateInfo into inlineInfo
inlineInfo.inlineCandidateInfo = inlineCandidateInfo;
- DWORD inlineDepth = 0;
- BYTE * candidateIL = inlineCandidateInfo->methInfo.ILCode;
- inlExpPtr expList = inlineInfo.iciStmt->gtStmt.gtInlineExpList;
- if (fgIsUnboundedInlineRecursion(expList, candidateIL, &inlineDepth))
+ DWORD inlineDepth = 0;
+ BYTE* candidateIL = inlineCandidateInfo->methInfo.ILCode;
+ InlineContext* inlineContext = inlineInfo.iciStmt->gtStmt.gtInlineContext;
+
+ if (fgIsUnboundedInlineRecursion(inlineContext, candidateIL, &inlineDepth))
{
#ifdef DEBUG
if (verbose)
#endif // defined(DEBUG) || MEASURE_INLINING
//
- // Obtain an inlExpLst struct and update the gtInlineExpList field in the inlinee's statements
+ // Obtain the current InlineContext and link in a new child for the callee
//
- inlExpLst* expDsc = new (this, CMK_Inlining) inlExpLst;
- inlExpLst *parentLst = iciStmt->gtStmt.gtInlineExpList;
- noway_assert(parentLst != nullptr);
- BYTE *parentIL = pInlineInfo->inlineCandidateInfo->methInfo.ILCode;
- expDsc->ixlCode = parentIL;
- expDsc->ixlParent = parentLst;
+ InlineContext* calleeContext = new (this, CMK_Inlining) InlineContext;
+ InlineContext* parentContext = iciStmt->gtStmt.gtInlineContext;
+ noway_assert(parentContext != nullptr);
+ BYTE* calleeIL = pInlineInfo->inlineCandidateInfo->methInfo.ILCode;
+ calleeContext->inlCode = calleeIL;
+ calleeContext->inlParent = parentContext;
// Push on front here will put siblings in reverse lexical
// order which we undo in the dumper
- expDsc->ixlSibling = parentLst->ixlChild;
- parentLst->ixlChild = expDsc;
- expDsc->ixlChild = nullptr;
- expDsc->ilOffset = iciStmt->AsStmt()->gtStmtILoffsx;
+ calleeContext->inlSibling = parentContext->inlChild;
+ parentContext->inlChild = calleeContext;
+ calleeContext->inlChild = nullptr;
+ calleeContext->inlOffset = iciStmt->AsStmt()->gtStmtILoffsx;
+ calleeContext->inlObservation = pInlineInfo->inlineResult->getObservation();
#ifdef DEBUG
- expDsc->methodName = eeGetMethodFullName(pInlineInfo->fncHandle);
- expDsc->depth = parentLst->depth + 1;
+ calleeContext->inlCallee = pInlineInfo->fncHandle;
#endif
for (block = InlineeCompiler->fgFirstBB;
stmt;
stmt = stmt->gtNextStmt)
{
- stmt->gtInlineExpList = expDsc;
+ stmt->gtInlineContext = calleeContext;
}
}
node->gtCall.gtEntryPoint.addr = nullptr;
#endif
+#ifdef DEBUG
+ node->gtCall.gtInlineObservation = InlineObservation::CALLEE_UNUSED_INITIAL;
+#endif
+
#ifdef DEBUGGING_SUPPORT
// Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code.
//
copy->gtCall.gtEntryPoint = tree->gtCall.gtEntryPoint;
#endif
+#ifdef DEBUG
+ copy->gtCall.gtInlineObservation = tree->gtCall.gtInlineObservation;
+#endif
+
break;
case GT_FIELD:
/* gtCall -- method call (GT_CALL) */
typedef class fgArgInfo * fgArgInfoPtr;
+enum class InlineObservation;
struct GenTreeCall final : public GenTree
{
#ifdef DEBUG
// For non-inline candidates, track the first observation
// that blocks candidacy.
- unsigned gtInlineObservation;
+ InlineObservation gtInlineObservation;
#endif
GenTreeCall(var_types type) :
/* gtStmt -- 'statement expr' (GT_STMT) */
+struct InlineContext;
+
struct GenTreeStmt: public GenTree
{
- GenTreePtr gtStmtExpr; // root of the expression tree
- GenTreePtr gtStmtList; // first node (for forward walks)
-
- inlExpPtr gtInlineExpList; // The inline expansion list of this statement.
- // This is a list of CORINFO_METHOD_HANDLEs
- // that shows the history of inline expansion
- // which leads to this statement.
+ GenTreePtr gtStmtExpr; // root of the expression tree
+ GenTreePtr gtStmtList; // first node (for forward walks)
+ InlineContext* gtInlineContext; // The inline context for this statement.
#if defined(DEBUGGING_SUPPORT) || defined(DEBUG)
IL_OFFSETX gtStmtILoffsx; // instr offset (if available)
: GenTree(GT_STMT, TYP_VOID)
, gtStmtExpr(expr)
, gtStmtList(nullptr)
- , gtInlineExpList(nullptr)
+ , gtInlineContext(nullptr)
#if defined(DEBUGGING_SUPPORT) || defined(DEBUG)
, gtStmtILoffsx(offset)
#endif
}
}
+
+//------------------------------------------------------------------------
+// InlieContext: default constructor
+//
+// Notes: use for the root instance. We set inlCode to nullptr here
+// (rather than the IL buffer address of the root method) to preserve
+// existing behavior, which is to allow one recursive inline.
+
+InlineContext::InlineContext()
+ : inlParent(nullptr)
+ , inlChild(nullptr)
+ , inlSibling(nullptr)
+ , inlOffset(BAD_IL_OFFSET)
+ , inlCode(nullptr)
+ , inlObservation(InlineObservation::CALLEE_UNUSED_INITIAL)
+#ifdef DEBUG
+ , inlCallee(nullptr)
+#endif
+{
+ // Empty
+}
+
+#ifdef DEBUG
+
+//------------------------------------------------------------------------
+// Dump: Dump an InlineContext entry and all descendants to stdout
+//
+// Arguments:
+// compiler - compiler instance doing inlining
+// indent - indentation level for this node
+
+void InlineContext::Dump(Compiler* compiler, int indent)
+{
+ // Handle fact that siblings are in reverse order.
+ if (inlSibling != nullptr)
+ {
+ inlSibling->Dump(compiler, indent);
+ }
+
+ const char* calleeName = compiler->eeGetMethodFullName(inlCallee);
+
+ // Dump this node
+ if (inlParent == nullptr)
+ {
+ // Root method
+ printf("Inlines into %s\n", calleeName);
+ }
+ else
+ {
+ // Successful inline
+ const char* inlineReason = inlGetDescriptionString(inlObservation);
+
+ for (int i = 0; i < indent; i++)
+ {
+ printf(" ");
+ }
+
+ if (inlOffset == BAD_IL_OFFSET)
+ {
+ printf("[IL=????] [%s] %s\n", inlineReason, calleeName);
+ }
+ else
+ {
+ IL_OFFSET offset = jitGetILoffs(inlOffset);
+ printf("[IL=%04d] [%s] %s\n", offset, inlineReason, calleeName);
+ }
+ }
+
+ // Recurse to first child
+ if (inlChild != nullptr)
+ {
+ inlChild->Dump(compiler, indent + 2);
+ }
+}
+
+#endif // DEBUG
+
//------------------------------------------------------------------------
// InlineResult: Construct an InlineResult to evaluate a particular call
// for inlining.
}
//------------------------------------------------------------------------
-// InlineResult: Construct an InlineResult to evaluate a particular
+// InlineResult: Construct an InlineResult to evaluate a particular
// method as a possible inline candidate.
//
// Notes:
// compiler should have revoked candidacy on the call by now
assert((inlCall->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);
- inlCall->gtInlineObservation = static_cast<unsigned>(inlObservation);
+ inlCall->gtInlineObservation = inlObservation;
}
#endif // DEBUG
// InlineTarget -- enum, target of a particular observation
// InlineImpact -- enum, impact of a particular observation
// InlineObservation -- enum, facts observed when considering an inline
+// InlineContext -- class, remembers what inlines happened
// InlineResult -- class, accumulates observations and makes a decision
// InlineCandidateInfo -- struct, detailed information needed for inlining
// InlArgInfo -- struct, information about a candidate's argument
InlineImpact inlGetImpact(InlineObservation obs);
+// InlineContext tracks the inline history in a method.
+//
+// Notes:
+//
+// InlineContexts form a tree with the root method as the root and
+// inlines as children. Nested inlines are represented as granchildren
+// and so on.
+//
+// Leaves in the tree represent successful inlines of leaf methods.
+// In DEBUG builds we also keep track of failed inline attempts.
+//
+// During inlining, all statements in the IR refer back to the
+// InlineContext that is responsible for those statements existing.
+// This makes it possible to detect recursion and to keep track of the
+// depth of each inline attempt.
+
+struct InlineContext
+{
+ // Default constructor, suitable for root instance
+ InlineContext();
+
+ InlineContext* inlParent; // logical caller (parent)
+ InlineContext* inlChild; // first child
+ InlineContext* inlSibling; // next child of the parent
+ IL_OFFSETX inlOffset; // call site location within parent
+ BYTE* inlCode; // address of IL buffer for the method
+ InlineObservation inlObservation; // what lead to this inline
+
+#ifdef DEBUG
+ CORINFO_METHOD_HANDLE inlCallee; // handle to the method
+
+ // Dump this entry and all descendants
+ void Dump(Compiler* compiler, int indent = 0);
+#endif
+};
+
// InlineResult summarizes what is known about the viability of a
// particular inline candiate.
report();
}
+ // The observation leading to this particular result
+ InlineObservation getObservation() const
+ {
+ return inlObservation;
+ }
+
// The reason for this particular result
const char * reasonString() const
{
// Try and recover the reason left behind when the jit decided
// this call was not a candidate.
- InlineObservation priorObservation = static_cast<InlineObservation>(call->gtInlineObservation);
+ InlineObservation priorObservation = call->gtInlineObservation;
+
if (inlIsValidObservation(priorObservation))
{
currentObservation = priorObservation;