compMaxUncheckedOffsetForNullObject = MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT;
- compNativeSizeEstimate = NATIVE_SIZE_INVALID;
-
for (unsigned i = 0; i < MAX_LOOP_NUM; i++)
{
AllVarSetOps::AssignNoCopy(this, optLoopTable[i].lpAsgVars, AllVarSetOps::UninitVal());
// the code in fgFindJumpTargets references that data
// member extensively.
assert(compInlineResult == nullptr);
+ assert(impInlineInfo == nullptr);
compInlineResult = &prejitResult;
// Find the basic blocks. We must do this regardless of
// inlineability, since we are prejitting this method.
//
- // Among other things, this will set compNativeSizeEstimate
- // for the subset of methods we check below.
+ // This will also update the status of this method as
+ // an inline candidate.
fgFindBasicBlocks();
// Undo the temporary setup.
assert(compInlineResult == &prejitResult);
compInlineResult = nullptr;
- // If this method is still a viable inline candidate,
- // do the profitability screening.
- if (prejitResult.IsCandidate())
- {
- // Only needed if the inline is discretionary.
- InlineObservation obs = prejitResult.GetObservation();
- if (obs == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE)
- {
- // We should have run the CodeSeq state machine
- // and got the native size estimate.
- assert(compNativeSizeEstimate != NATIVE_SIZE_INVALID);
-
- // Estimate the call site impact
- int callsiteNativeSizeEstimate =
- prejitResult.DetermineCallsiteNativeSizeEstimate(methodInfo);
-
- // See if we're willing to pay for inlining this method
- impCanInlineNative(callsiteNativeSizeEstimate,
- compNativeSizeEstimate,
- nullptr, // Calculate static inlining hint.
- &prejitResult);
- }
- }
- else
+ // If still a viable, discretionary inline, assess
+ // profitability.
+ if (prejitResult.IsDiscretionaryCandidate())
{
- // If it's not a candidate, it should be a failure.
- assert(prejitResult.IsFailure());
+ prejitResult.DetermineProfitability(methodInfo);
}
// Handle the results of the inline analysis.
static BOOL impIsAddressInLocal(GenTreePtr tree, GenTreePtr * lclVarTreeOut);
- void impCanInlineNative(int callsiteNativeEstimate,
- int calleeNativeSizeEstimate,
- InlineInfo* pInlineInfo,
- InlineResult* inlineResult);
+ void impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo,
+ InlineResult* inlineResult);
// STATIC inlining decision based on the IL code.
void impCanInlineIL(CORINFO_METHOD_HANDLE fncHandle,
// This can be overwritten by setting complus_JITInlineSize env variable.
#define IMPLEMENTATION_MAX_INLINE_SIZE _UI16_MAX // Maximum method size supported by this implementation
-#define NATIVE_SIZE_INVALID (-10000)
-
- int compNativeSizeEstimate; // The estimated native size of this method.
-
private:
#ifdef FEATURE_JIT_METHOD_PERF
JitTimer* pCompJitTimer; // Timer data structure (by phases) for current compilation.
unsigned slot1;
unsigned depth;
};
+
//------------------------------------------------------------------------
// fgFindJumpTargets: walk the IL stream, determining jump target offsets
//
{
compInlineResult->Note(InlineObservation::CALLEE_END_OPCODE_SCAN);
- // If the inline is viable and discretionary, we need to get
- // an estimate for the callee native code size.
- if (compInlineResult->IsCandidate()
- && (compInlineResult->GetObservation() == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE))
+ // If the inline is viable and discretionary, do the
+ // profitability screening.
+ if (compInlineResult->IsDiscretionaryCandidate())
{
- compNativeSizeEstimate = compInlineResult->DetermineNativeSizeEstimate();
- noway_assert(compNativeSizeEstimate != NATIVE_SIZE_INVALID);
- JITDUMP("\n\ncompNativeSizeEstimate=%d\n", compNativeSizeEstimate);
+ // Make some callsite specific observations that will feed
+ // into the profitability model.
+ impMakeDiscretionaryInlineObservations(impInlineInfo, compInlineResult);
+
+ // None of those observations should have changed the
+ // inline's viability.
+ assert(compInlineResult->IsCandidate());
- // If we're inlining, use the size estimate to do further
- // screening.
- //
- // If we're in the prejit-root case we do something
- // similar as part of the prejit screen over in
- // compCompileHelper.
if (compIsForInlining())
{
- // Make an inlining decision based on the estimated native size.
- int callsiteNativeSizeEstimate =
- compInlineResult->DetermineCallsiteNativeSizeEstimate(
- &impInlineInfo->inlineCandidateInfo->methInfo);
-
- impCanInlineNative(callsiteNativeSizeEstimate,
- compNativeSizeEstimate,
- impInlineInfo,
- compInlineResult);
-
+ // Assess profitability...
+ CORINFO_METHOD_INFO* methodInfo = &impInlineInfo->inlineCandidateInfo->methInfo;
+ compInlineResult->DetermineProfitability(methodInfo);
+
if (compInlineResult->IsFailure())
{
-#ifdef DEBUG
- if (verbose)
- {
- printf("\n\nInline expansion aborted because of impCanInlineNative: %s %s\n",
- compInlineResult->ResultString(),
- compInlineResult->ReasonString());
- }
-#endif
-
+ JITDUMP("\n\nInline expansion aborted, inline not profitable\n");
return;
}
+ else
+ {
+ // The inline is still viable.
+ assert(compInlineResult->IsCandidate());
+ }
+ }
+ else
+ {
+ // Prejit root case. Profitability assessment for this
+ // is done over in compCompileHelper.
}
}
}
}
}
-/*****************************************************************************
- */
+//------------------------------------------------------------------------
+// impMakeDiscretionaryInlineObservations: make observations that help
+// determine the profitability of a discretionary inline
+//
+// Arguments:
+// pInlineInfo -- InlineInfo for the inline, or null for the prejit root
+// inlineResult -- InlineResult accumulating information about this inline
+//
+// Notes:
+// If inlining or prejitting the root, this method also makes
+// various observations about the method that factor into inline
+// decisions. It sets `compNativeSizeEstimate` as a side effect.
-void Compiler::impCanInlineNative(int callsiteNativeEstimate,
- int calleeNativeSizeEstimate,
- InlineInfo* pInlineInfo, // NULL for static inlining hint for ngen.
- InlineResult* inlineResult)
+void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo,
+ InlineResult* inlineResult)
{
assert(pInlineInfo != NULL && compIsForInlining() || // Perform the actual inlining.
pInlineInfo == NULL && !compIsForInlining() // Calculate the static inlining hint for ngen.
// shouldn't be relying on the result of this method.
assert(inlineResult->GetObservation() == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
- JITDUMP("\ncalleeNativeSizeEstimate=%d, callsiteNativeEstimate=%d.\n", calleeNativeSizeEstimate, callsiteNativeEstimate);
-
// Note if this method is an instance constructor
if ((info.compFlags & CORINFO_FLG_CONSTRUCTOR) != 0 &&
(info.compFlags & CORINFO_FLG_STATIC) == 0)
}
inlineResult->NoteInt(InlineObservation::CALLSITE_FREQUENCY, static_cast<int>(frequency));
-
- // Determine multiplier given the various observations.
- double multiplier = inlineResult->DetermineMultiplier();
-
- // Note the various estimates we've obtained.
- inlineResult->NoteInt(InlineObservation::CALLEE_NATIVE_SIZE_ESTIMATE, calleeNativeSizeEstimate);
- inlineResult->NoteInt(InlineObservation::CALLSITE_NATIVE_SIZE_ESTIMATE, callsiteNativeEstimate);
- inlineResult->NoteDouble(InlineObservation::CALLSITE_BENEFIT_MULTIPLIER, multiplier);
-
- int threshold = (int)(callsiteNativeEstimate * multiplier);
-
- JITDUMP("\ncalleeNativeSizeEstimate=%d, threshold=%d.\n", calleeNativeSizeEstimate, threshold);
-
-#define NATIVE_CALL_SIZE_MULTIPLIER (10.0)
- if (calleeNativeSizeEstimate > threshold)
- {
-#ifdef DEBUG
- char * message = (char *)compAllocator->allocateMemory(128);
- sprintf(message, "Native estimate for function size exceeds threshold %g > %g (multiplier = %g).",
- calleeNativeSizeEstimate / NATIVE_CALL_SIZE_MULTIPLIER,
- threshold / NATIVE_CALL_SIZE_MULTIPLIER, multiplier);
- //pInlineInfo is null when we're determining the static hint for inlining.
-#else
- const char * message = "Native estimate for function size exceeds threshold.";
-#endif
-
- if (pInlineInfo != nullptr)
- {
- inlineResult->NoteFatal(InlineObservation::CALLSITE_EXCEEDS_THRESHOLD);
- }
- else
- {
- // Static hint case.... here the "callee" is the function being assessed.
- inlineResult->NoteFatal(InlineObservation::CALLEE_EXCEEDS_THRESHOLD);
- }
- }
- else
- {
- JITLOG((LL_INFO100000, "Native estimate for function size is within threshold for inlining %g <= %g (multiplier = %g)\n",
- calleeNativeSizeEstimate / NATIVE_CALL_SIZE_MULTIPLIER,
- threshold / NATIVE_CALL_SIZE_MULTIPLIER, multiplier));
-
- // Candidate has passed the profitability screen, update candidacy.
- inlineResult->Note(InlineObservation::CALLSITE_NATIVE_SIZE_ESTIMATE_OK);
- }
-
-#undef NATIVE_CALL_SIZE_MULTIPLIER
}
INLINE_OBSERVATION(HAS_EH, bool, "has exception handling", FATAL, CALLEE)
INLINE_OBSERVATION(HAS_ENDFILTER, bool, "has endfilter", FATAL, CALLEE)
INLINE_OBSERVATION(HAS_ENDFINALLY, bool, "has endfinally", FATAL, CALLEE)
-INLINE_OBSERVATION(HAS_LEAVE, bool, "has leave" , FATAL, CALLEE)
+INLINE_OBSERVATION(HAS_LEAVE, bool, "has leave", FATAL, CALLEE)
INLINE_OBSERVATION(HAS_MANAGED_VARARGS, bool, "managed varargs", FATAL, CALLEE)
INLINE_OBSERVATION(HAS_NATIVE_VARARGS, bool, "native varargs", FATAL, CALLEE)
INLINE_OBSERVATION(HAS_NO_BODY, bool, "has no body", FATAL, CALLEE)
INLINE_OBSERVATION(MAXSTACK_TOO_BIG, bool, "maxstack too big" , FATAL, CALLEE)
INLINE_OBSERVATION(NEEDS_SECURITY_CHECK, bool, "needs security check", FATAL, CALLEE)
INLINE_OBSERVATION(NO_METHOD_INFO, bool, "cannot get method info", FATAL, CALLEE)
+INLINE_OBSERVATION(NOT_PROFITABLE_INLINE, bool, "unprofitable inline", FATAL, CALLEE)
INLINE_OBSERVATION(RETURN_TYPE_IS_COMPOSITE, bool, "has composite return type", FATAL, CALLEE)
INLINE_OBSERVATION(STACK_CRAWL_MARK, bool, "uses stack crawl mark", FATAL, CALLEE)
INLINE_OBSERVATION(STFLD_NEEDS_HELPER, bool, "stfld needs helper", FATAL, CALLEE)
INLINE_OBSERVATION(IS_FORCE_INLINE, bool, "aggressive inline attribute", INFORMATION, CALLEE)
INLINE_OBSERVATION(IS_INSTANCE_CTOR, bool, "instance constructor", INFORMATION, CALLEE)
INLINE_OBSERVATION(IS_MOSTLY_LOAD_STORE, bool, "method is mostly load/store", INFORMATION, CALLEE)
+INLINE_OBSERVATION(IS_PROFITABLE_INLINE, bool, "profitable inline", INFORMATION, CALLEE)
INLINE_OBSERVATION(LOOKS_LIKE_WRAPPER, bool, "thin wrapper around a call", INFORMATION, CALLEE)
INLINE_OBSERVATION(MAXSTACK, int, "maxstack", INFORMATION, CALLEE)
-INLINE_OBSERVATION(NATIVE_SIZE_ESTIMATE, double, "native size estimate", INFORMATION, CALLEE)
INLINE_OBSERVATION(OPCODE, int, "next opcode in IL stream", INFORMATION, CALLEE)
INLINE_OBSERVATION(OPCODE_NORMED, int, "next opcode in IL stream", INFORMATION, CALLEE)
INLINE_OBSERVATION(NUMBER_OF_ARGUMENTS, int, "number of arguments", INFORMATION, CALLEE)
INLINE_OBSERVATION(LDFLD_NEEDS_HELPER, bool, "ldfld needs helper", FATAL, CALLSITE)
INLINE_OBSERVATION(LDVIRTFN_ON_NON_VIRTUAL, bool, "ldvirtfn on non-virtual", FATAL, CALLSITE)
INLINE_OBSERVATION(NOT_CANDIDATE, bool, "not inline candidate", FATAL, CALLSITE)
+INLINE_OBSERVATION(NOT_PROFITABLE_INLINE, bool, "unprofitable inline", FATAL, CALLSITE)
INLINE_OBSERVATION(REQUIRES_SAME_THIS, bool, "requires same this", FATAL, CALLSITE)
INLINE_OBSERVATION(RETURN_TYPE_MISMATCH, bool, "return type mismatch", FATAL, CALLSITE)
INLINE_OBSERVATION(STFLD_NEEDS_HELPER, bool, "stfld needs helper", FATAL, CALLSITE)
// ------ Call Site Information -------
-INLINE_OBSERVATION(BENEFIT_MULTIPLIER, double, "benefit multiplier", INFORMATION, CALLSITE)
INLINE_OBSERVATION(CONSTANT_ARG_FEEDS_TEST, bool, "constant argument feeds test", INFORMATION, CALLSITE)
INLINE_OBSERVATION(DEPTH, int, "depth", INFORMATION, CALLSITE)
INLINE_OBSERVATION(FREQUENCY, int, "execution frequency", INFORMATION, CALLSITE)
-INLINE_OBSERVATION(NATIVE_SIZE_ESTIMATE, double, "native size estimate", INFORMATION, CALLSITE)
-INLINE_OBSERVATION(NATIVE_SIZE_ESTIMATE_OK, bool, "native size estimate ok", INFORMATION, CALLSITE)
+INLINE_OBSERVATION(IS_PROFITABLE_INLINE, bool, "profitable inline", INFORMATION, CALLSITE)
// ------ Final Sentinel -------
virtual void NoteDouble(InlineObservation obs, double value) = 0;
// Policy determinations
- virtual double DetermineMultiplier() = 0;
- virtual int DetermineNativeSizeEstimate() = 0;
- virtual int DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methodInfo) = 0;
+ virtual void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) = 0;
// Policy policies
virtual bool PropagateNeverToRuntime() const = 0;
return InlDecisionIsCandidate(m_Policy->GetDecision());
}
+ // Has the policy determined this inline attempt is still viable
+ // and is a discretionary inline?
+ bool IsDiscretionaryCandidate() const
+ {
+ bool result = InlDecisionIsCandidate(m_Policy->GetDecision()) &&
+ (m_Policy->GetObservation() == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
+
+ return result;
+ }
+
// Has the policy made a determination?
bool IsDecided() const
{
m_Policy->NoteDouble(obs, value);
}
- // Determine the benfit multiplier for this inline.
- double DetermineMultiplier()
- {
- return m_Policy->DetermineMultiplier();
- }
-
- // Determine the native size estimate for this inline
- int DetermineNativeSizeEstimate()
- {
- return m_Policy->DetermineNativeSizeEstimate();
- }
-
- // Determine the native size estimate for this call site
- int DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methodInfo)
+ // Determine if this inline is profitable
+ void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
{
- return m_Policy->DetermineCallsiteNativeSizeEstimate(methodInfo);
+ return m_Policy->DetermineProfitability(methodInfo);
}
// Ensure details of this inlining process are appropriately
case InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST:
m_ConstantFeedsConstantTest = value;
break;
- case InlineObservation::CALLSITE_NATIVE_SIZE_ESTIMATE_OK:
- // Passed the profitability screen. Update candidacy.
- SetCandidate(obs);
- break;
case InlineObservation::CALLEE_BEGIN_OPCODE_SCAN:
{
// Set up the state machine, if this inline is
case InlineObservation::CALLEE_END_OPCODE_SCAN:
{
- // We only expect to see this observation once, so the
- // native size estimate should have its initial value.
- assert(m_NativeSizeEstimate == NATIVE_SIZE_INVALID);
-
- // If we were using the state machine, get it to kick
- // out a size estimate.
if (m_StateMachine != nullptr)
{
m_StateMachine->End();
- m_NativeSizeEstimate = m_StateMachine->NativeSize;
- assert(m_NativeSizeEstimate != NATIVE_SIZE_INVALID);
}
break;
int LegacyPolicy::DetermineNativeSizeEstimate()
{
// Should be a discretionary candidate.
- assert(m_Observation == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
- // Should have a valid state machine estimate.
- assert(m_NativeSizeEstimate != NATIVE_SIZE_INVALID);
+ assert(m_StateMachine != nullptr);
- return m_NativeSizeEstimate;
+ return m_StateMachine->NativeSize;
}
//------------------------------------------------------------------------
return callsiteSize;
}
+
+//------------------------------------------------------------------------
+// DetermineProfitability: determine if this inline is profitable
+//
+// Arguments:
+// methodInfo -- method info for the callee
+//
+// Notes:
+// A profitable inline is one that is projected to have a beneficial
+// size/speed tradeoff.
+//
+// It is expected that this method is only invoked for discretionary
+// candidates, since it does not make sense to do this assessment for
+// failed, always, or forced inlines.
+
+void LegacyPolicy::DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
+{
+ assert(InlDecisionIsCandidate(m_Decision));
+ assert(m_Observation == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
+
+ const int calleeNativeSizeEstimate = DetermineNativeSizeEstimate();
+ const int callsiteNativeSizeEstimate = DetermineCallsiteNativeSizeEstimate(methodInfo);
+ const double multiplier = DetermineMultiplier();
+ const int threshold = (int)(callsiteNativeSizeEstimate * multiplier);
+
+ JITDUMP("calleeNativeSizeEstimate=%d\n", calleeNativeSizeEstimate)
+ JITDUMP("callsiteNativeSizeEstimate=%d\n", callsiteNativeSizeEstimate);
+ JITDUMP("benefit multiplier=%g\n", multiplier);
+ JITDUMP("threshold=%d\n", threshold);
+
+#if DEBUG
+ // Size estimates are in bytes * 10
+ const double sizeDescaler = 10.0;
+#endif
+
+ // Reject if callee size is over the threshold
+ if (calleeNativeSizeEstimate > threshold)
+ {
+ // Inline appears to be unprofitable
+ JITLOG_THIS(m_Compiler,
+ (LL_INFO100000,
+ "Native estimate for function size exceedsn threshold"
+ " for inlining %g > %g (multiplier = %g)\n",
+ calleeNativeSizeEstimate / sizeDescaler,
+ threshold / sizeDescaler,
+ multiplier));
+
+ // Fail the inline
+ if (m_IsPrejitRoot)
+ {
+ SetNever(InlineObservation::CALLEE_NOT_PROFITABLE_INLINE);
+ }
+ else
+ {
+ SetFailure(InlineObservation::CALLSITE_NOT_PROFITABLE_INLINE);
+ }
+ }
+ else
+ {
+ // Inline appears to be profitable
+ JITLOG_THIS(m_Compiler,
+ (LL_INFO100000,
+ "Native estimate for function size is within threshold"
+ " for inlining %g <= %g (multiplier = %g)\n",
+ calleeNativeSizeEstimate / sizeDescaler,
+ threshold / sizeDescaler,
+ multiplier));
+
+ // Update candidacy
+ if (m_IsPrejitRoot)
+ {
+ SetCandidate(InlineObservation::CALLEE_IS_PROFITABLE_INLINE);
+ }
+ else
+ {
+ SetCandidate(InlineObservation::CALLSITE_IS_PROFITABLE_INLINE);
+ }
+ }
+}
, m_Compiler(compiler)
, m_StateMachine(nullptr)
, m_CodeSize(0)
- , m_NativeSizeEstimate(NATIVE_SIZE_INVALID)
, m_CallsiteFrequency(InlineCallsiteFrequency::UNUSED)
, m_IsForceInline(false)
, m_IsForceInlineKnown(false)
void NoteDouble(InlineObservation obs, double value) override;
// Policy determinations
- double DetermineMultiplier() override;
- int DetermineNativeSizeEstimate() override;
- int DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methodInfo) override;
+ void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
// Policy policies
bool PropagateNeverToRuntime() const override { return true; }
void SetCandidate(InlineObservation obs);
void SetFailure(InlineObservation obs);
void SetNever(InlineObservation obs);
+ double DetermineMultiplier();
+ int DetermineNativeSizeEstimate();
+ int DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methodInfo);
// Constants
const unsigned MAX_BASIC_BLOCKS = 5;
Compiler* m_Compiler;
CodeSeqSM* m_StateMachine;
unsigned m_CodeSize;
- int m_NativeSizeEstimate;
InlineCallsiteFrequency m_CallsiteFrequency;
bool m_IsForceInline :1;
bool m_IsForceInlineKnown :1;