Refactor the InlineResult to take a `GenTreeCall` instead of artifacts
derived from the call. Use this to decorate the call (in DEBUG) if
an inline fails with the observation that lead to the failure. Move
this constructor out of the header since we now need it to invoke
methods on types that are header-opaque.
Try and pick this reason up later on when non-candidate call sites are
encountered during inlining.
Introduce a second constructor for the pre-jit use case, where we are
evaulating a method to see if we can mark it as never inline to save
work in any subsequent compilation. Put this into the cpp file too for
symmetry.
Type the backing field in GenTreeCall as unsigned to avoid creating
more deeply entangled include circularities. Happy to reconsider if
this seems ill-advised.
Reword a few more uses of inlinee to callee (similarly inliner to
caller). Make `inlIsValidObservation` globally visible and enable
a prior commented-out assert.
Commit migrated from https://github.com/dotnet/coreclr/commit/
df9c1264c5252cdd6154f778dca9cb7e01c3cea9
if (opts.eeFlags & CORJIT_FLG_PREJIT)
{
// Cache inlining hint during NGen to avoid touching bodies of non-inlineable methods at runtime
- InlineResult trialResult(this, nullptr, methodHnd, "prejit1");
+ InlineResult trialResult(this, methodHnd, "prejit1");
impCanInlineIL(methodHnd, methodInfo, forceInline, &trialResult);
if (trialResult.isFailure())
{
assert(compNativeSizeEstimate != NATIVE_SIZE_INVALID);
int callsiteNativeSizeEstimate = impEstimateCallsiteNativeSize(methodInfo);
- InlineResult trialResult(this, nullptr, methodHnd, "prejit2");
+ InlineResult trialResult(this, methodHnd, "prejit2");
impCanInlineNative(callsiteNativeSizeEstimate,
compNativeSizeEstimate,
CORINFO_CONST_LOOKUP gtEntryPoint;
#endif
+#ifdef DEBUG
+ // For non-inline candidates, track the first observation
+ // that blocks candidacy.
+ unsigned gtInlineObservation;
+#endif
+
GenTreeCall(var_types type) :
GenTree(GT_CALL, type)
{
}
else
{
- // Static hint case.... here the "callee" is the function being assessed. (PLS VERIFY)
+ // Static hint case.... here the "callee" is the function being assessed.
inlineResult->noteFatal(InlineObservation::CALLEE_EXCEEDS_THRESHOLD);
}
}
}
GenTreeCall* call = callNode->AsCall();
- CORINFO_METHOD_HANDLE callerHandle = info.compMethodHnd;
- CORINFO_METHOD_HANDLE calleeHandle = call->gtCall.gtCallType == CT_USER_FUNC ? call->gtCall.gtCallMethHnd : nullptr;
- InlineResult inlineResult(this, callerHandle, calleeHandle, "impMarkInlineCandidate");
+ InlineResult inlineResult(this, call, "impMarkInlineCandidate");
/* Don't inline if not optimized code */
if (opts.compDbgCode)
// Return Value:
// true if the observation is valid
-static bool inlIsValidObservation(InlineObservation obs)
+bool inlIsValidObservation(InlineObservation obs)
{
return((obs > InlineObservation::CALLEE_UNUSED_INITIAL) &&
(obs < InlineObservation::CALLEE_UNUSED_FINAL));
}
//------------------------------------------------------------------------
+// InlineResult: Construct an InlineResult to evaluate a particular call
+// for inlining.
+//
+// Arguments
+// compiler - the compiler instance examining an call for ininling
+// call - the call in question
+// context - descrptive string to describe the context of the decision
+
+InlineResult::InlineResult(Compiler* compiler,
+ GenTreeCall* call,
+ const char* context)
+ : inlCompiler(compiler)
+ , inlDecision(InlineDecision::UNDECIDED)
+ , inlObservation(InlineObservation::CALLEE_UNUSED_INITIAL)
+ , inlCall(call)
+ , inlCaller(nullptr)
+ , inlCallee(nullptr)
+ , inlContext(context)
+ , inlReported(false)
+{
+ // Get method handle for caller
+ inlCaller = inlCompiler->info.compMethodHnd;
+
+ // Get method handle for callee, if known
+ if (inlCall->gtCall.gtCallType == CT_USER_FUNC)
+ {
+ inlCallee = call->gtCall.gtCallMethHnd;
+ }
+}
+
+//------------------------------------------------------------------------
+// InlineResult: Construct an InlineResult to evaluate a particular
+// method as a possible inline candidate.
+//
+// Notes:
+// Used only during prejitting to try and pre-identify methods
+// that cannot be inlined, to help subsequent jit throughput.
+//
+// We use the inlCallee member to track the method since logically
+// it is the callee here.
+//
+// Arguments
+// compiler - the compiler instance doing the prejitting
+// method - the method in question
+// context - descrptive string to describe the context of the decision
+
+InlineResult::InlineResult(Compiler* compiler,
+ CORINFO_METHOD_HANDLE method,
+ const char* context)
+ : inlCompiler(compiler)
+ , inlDecision(InlineDecision::UNDECIDED)
+ , inlObservation(InlineObservation::CALLEE_UNUSED_INITIAL)
+ , inlCall(nullptr)
+ , inlCaller(nullptr)
+ , inlCallee(method)
+ , inlContext(context)
+ , inlReported(false)
+{
+ // Empty
+}
+
+//------------------------------------------------------------------------
// report: Dump, log, and report information about an inline decision.
//
// Notes:
#ifdef DEBUG
+ // Optionally dump the result
if (VERBOSE)
{
const char* format = "INLINER: during '%s' result '%s' reason '%s' for '%s' calling '%s'\n";
- const char* caller = (inlInliner == nullptr) ? "n/a" : inlCompiler->eeGetMethodFullName(inlInliner);
- const char* callee = (inlInlinee == nullptr) ? "n/a" : inlCompiler->eeGetMethodFullName(inlInlinee);
+ const char* caller = (inlCaller == nullptr) ? "n/a" : inlCompiler->eeGetMethodFullName(inlCaller);
+ const char* callee = (inlCallee == nullptr) ? "n/a" : inlCompiler->eeGetMethodFullName(inlCallee);
JITDUMP(format, inlContext, resultString(), reasonString(), caller, callee);
}
+ // If the inline failed, leave information on the call so we can
+ // later recover what observation lead to the failure.
+ if (isFailure() && (inlCall != nullptr))
+ {
+ // compiler should have revoked candidacy on the call by now
+ assert((inlCall->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);
+
+ inlCall->gtInlineObservation = static_cast<unsigned>(inlObservation);
+ }
+
#endif // DEBUG
- if (isDecided())
+ if (isDecided())
{
const char* format = "INLINER: during '%s' result '%s' reason '%s'\n";
JITLOG_THIS(inlCompiler, (LL_INFO100000, format, inlContext, resultString(), reasonString()));
COMP_HANDLE comp = inlCompiler->info.compCompHnd;
- comp->reportInliningDecision(inlInliner, inlInlinee, result(), reasonString());
+ comp->reportInliningDecision(inlCaller, inlCallee, result(), reasonString());
}
}
#undef INLINE_OBSERVATION
};
+#ifdef DEBUG
+
+// Sanity check the observation value
+
+bool inlIsValidObservation(InlineObservation obs);
+
+#endif // DEBUG
+
// Get a string describing this observation
const char* inlGetDescriptionString(InlineObservation obs);
{
public:
- // Construct a new InlineResult.
+ // Construct a new InlineResult to help evaluate a
+ // particular call for inlining.
InlineResult(Compiler* compiler,
- CORINFO_METHOD_HANDLE inliner,
- CORINFO_METHOD_HANDLE inlinee,
- const char* context)
- : inlCompiler(compiler)
- , inlDecision(InlineDecision::UNDECIDED)
- , inlObservation(InlineObservation::CALLEE_UNUSED_INITIAL)
- , inlInliner(inliner)
- , inlInlinee(inlinee)
- , inlContext(context)
- , inlReported(false)
- {
- // empty
- }
+ GenTreeCall* call,
+ const char* context);
+
+ // Construct a new InlineResult to evaluate a particular
+ // method to see if it is inlineable.
+ InlineResult(Compiler* compiler,
+ CORINFO_METHOD_HANDLE method,
+ const char* context);
// Translate into CorInfoInline for reporting back to the runtime.
//
}
// The reason for this particular result
- const char * reasonString() const
- {
- return inlGetDescriptionString(inlObservation);
+ const char * reasonString() const
+ {
+ return inlGetDescriptionString(inlObservation);
}
// setReported indicates that this particular result doesn't need
// Helper for setting decision and reason
void setCommon(InlineDecision decision, InlineObservation obs)
{
- // assert(inlIsValidObservation(obs));
+ assert(inlIsValidObservation(obs));
assert(decision != InlineDecision::UNDECIDED);
inlDecision = decision;
inlObservation = obs;
Compiler* inlCompiler;
InlineDecision inlDecision;
InlineObservation inlObservation;
- CORINFO_METHOD_HANDLE inlInliner;
- CORINFO_METHOD_HANDLE inlInlinee;
+ GenTreeCall* inlCall;
+ CORINFO_METHOD_HANDLE inlCaller;
+ CORINFO_METHOD_HANDLE inlCallee;
const char* inlContext;
bool inlReported;
};
GenTreeCall* call = node->AsCall();
// Prepare to record information about this inline
- CORINFO_METHOD_HANDLE callerHandle = call->gtCall.gtInlineCandidateInfo->ilCallerHandle;
- CORINFO_METHOD_HANDLE calleeHandle = call->gtCall.gtCallType == CT_USER_FUNC ? call->gtCall.gtCallMethHnd : nullptr;
- InlineResult inlineResult(this, callerHandle, calleeHandle, "fgMorphCallInline");
+ InlineResult inlineResult(this, call, "fgMorphCallInline");
// Attempt the inline
fgMorphCallInlineHelper(call, &inlineResult);
if ((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0)
{
+ InlineObservation currentObservation = InlineObservation::CALLSITE_NOT_CANDIDATE;
+
+#ifdef DEBUG
+
+ // Try and recover the reason left behind when the jit decided
+ // this call was not a candidate.
+ InlineObservation priorObservation = static_cast<InlineObservation>(call->gtInlineObservation);
+ if (inlIsValidObservation(priorObservation))
+ {
+ currentObservation = priorObservation;
+ }
+#endif
+
result->noteFatal(InlineObservation::CALLSITE_NOT_CANDIDATE);
return;
}