if (compIsForInlining())
{
- if (forceInline)
- {
- compInlineResult->noteCandidate(InlineObservation::CALLEE_IS_FORCE_INLINE);
- }
-
compInlineResult->noteInt(InlineObservation::CALLEE_NUMBER_OF_BASIC_BLOCKS, fgBBcount);
if (compInlineResult->isFailure())
// Keep track of constants and args on the stack.
fgStack pushedStack;
+ // Observe force inline state and code size.
+ bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0;
+
+ if (compInlineResult != nullptr)
+ {
+ compInlineResult->noteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline);
+ compInlineResult->noteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize);
+ }
+
// Determine whether to start the state machine to estimate the size of the
// native code for this method.
bool useSm = false;
- if ((codeSize > ALWAYS_INLINE_SIZE) &&
- !(info.compFlags & CORINFO_FLG_FORCEINLINE))
+ if ((codeSize > ALWAYS_INLINE_SIZE) && !isForceInline)
{
// The size of the native code for this method matters for inlining
// decisions.
}
}
}
- else
- {
- if (compIsForInlining())
- {
- // This method's IL was small enough that we didn't use the size model to estimate
- // inlinability. Note that as the latest candidate reason.
- compInlineResult->noteCandidate(InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE);
- }
- }
-
if (!compIsForInlining() && // None of the local vars in the inlinee should have address taken or been written to.
// Therefore we should NOT need to enter this "if" statement.
calleeNativeSizeEstimate / NATIVE_CALL_SIZE_MULTIPLIER,
threshold / NATIVE_CALL_SIZE_MULTIPLIER, multiplier));
- // Still a viable candidate....update status
- inlineResult->noteCandidate(InlineObservation::CALLSITE_NATIVE_SIZE_ESTIMATE_OK);
+ // Candidate has passed the profitability screen, update candidacy.
+ inlineResult->note(InlineObservation::CALLSITE_NATIVE_SIZE_ESTIMATE_OK);
}
#undef NATIVE_CALL_SIZE_MULTIPLIER
return;
}
- if (forceInline)
- {
- inlineResult->noteCandidate(InlineObservation::CALLEE_IS_FORCE_INLINE);
- }
-
// Reject if it has too many locals.
// This is currently an implementation limit due to fixed-size arrays in the
// inline info, rather than a performance heuristic.
return;
}
- // Reject large functions
+ // Note force inline state
- inlineResult->noteInt(InlineObservation::CALLEE_NUMBER_OF_IL_BYTES, codeSize);
+ inlineResult->noteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, forceInline);
+
+ // Note IL code size
+
+ inlineResult->noteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize);
if (inlineResult->isFailure())
{
{
return;
}
-
- // Still a viable candidate...
- inlineResult->noteCandidate(InlineObservation::CALLEE_CAN_INLINE_IL);
}
/*****************************************************************************
pInfo->initClassResult = initClassResult;
*(pParam->ppInlineCandidateInfo) = pInfo;
-
- pParam->result->noteCandidate(InlineObservation::CALLEE_CHECK_CAN_INLINE_IL);
_exit:
;
printf("\n");
}
#endif
-
- //
- // The current arg does not prevent inlining.
- //
- // This doesn't mean that other information or other args
- // will not prevent inlining of this method.
- //
- inlineResult->noteCandidate(InlineObservation::CALLSITE_ARGS_OK);
}
/*****************************************************************************
pInlineInfo->hasSIMDTypeArgLocalOrReturn = foundSIMDType;
#endif // FEATURE_SIMD
- inlineResult->noteCandidate(InlineObservation::CALLSITE_LOCALS_OK);
}
, inlReported(false)
{
// Set the policy
- inlPolicy = InlinePolicy::getPolicy(compiler);
+ const bool isPrejitRoot = false;
+ inlPolicy = InlinePolicy::getPolicy(compiler, isPrejitRoot);
// Get method handle for caller
inlCaller = inlCompiler->info.compMethodHnd;
//------------------------------------------------------------------------
// InlineResult: Construct an InlineResult to evaluate a particular
-// method as a possible inline candidate.
+// method as a possible inline candidate, while prejtting.
//
// Arguments:
// compiler - the compiler instance doing the prejitting
, inlReported(false)
{
// Set the policy
- inlPolicy = InlinePolicy::getPolicy(compiler);
+ const bool isPrejitRoot = true;
+ inlPolicy = InlinePolicy::getPolicy(compiler, isPrejitRoot);
}
//------------------------------------------------------------------------
INLINE_OBSERVATION(ARG_FEEDS_CONSTANT_TEST, bool, "argument feeds constant test", INFORMATION, CALLEE)
INLINE_OBSERVATION(ARG_FEEDS_RANGE_CHECK, bool, "argument feeds range check", INFORMATION, CALLEE)
INLINE_OBSERVATION(BELOW_ALWAYS_INLINE_SIZE, bool, "below ALWAYS_INLINE size", INFORMATION, CALLEE)
-INLINE_OBSERVATION(CAN_INLINE_IL, bool, "IL passes basic checks", INFORMATION, CALLEE)
-INLINE_OBSERVATION(CHECK_CAN_INLINE_IL, bool, "IL passes detailed checks", INFORMATION, CALLEE)
INLINE_OBSERVATION(CLASS_PROMOTABLE, bool, "promotable value class", INFORMATION, CALLEE)
INLINE_OBSERVATION(HAS_SIMD, bool, "has SIMD arg, local, or ret", INFORMATION, CALLEE)
INLINE_OBSERVATION(HAS_SWITCH, bool, "has switch", INFORMATION, CALLEE)
+INLINE_OBSERVATION(IL_CODE_SIZE, int, "number of bytes of IL", INFORMATION, CALLEE)
+INLINE_OBSERVATION(IS_DISCRETIONARY_INLINE, bool, "can inline, check heuristics", INFORMATION, 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(LOOKS_LIKE_WRAPPER, bool, "thin wrapper around a call", INFORMATION, CALLEE)
INLINE_OBSERVATION(MAXSTACK, int, "maxstack", INFORMATION, CALLEE)
-INLINE_OBSERVATION(IS_MOSTLY_LOAD_STORE, bool, "method is mostly load/store", INFORMATION, CALLEE)
INLINE_OBSERVATION(NATIVE_SIZE_ESTIMATE, double, "native size estimate", INFORMATION, CALLEE)
INLINE_OBSERVATION(NUMBER_OF_ARGUMENTS, int, "number of arguments", INFORMATION, CALLEE)
INLINE_OBSERVATION(NUMBER_OF_BASIC_BLOCKS, int, "number of basic blocks", INFORMATION, CALLEE)
-INLINE_OBSERVATION(NUMBER_OF_IL_BYTES, int, "number of bytes of IL", INFORMATION, CALLEE)
INLINE_OBSERVATION(NUMBER_OF_LOCALS, int, "number of locals", INFORMATION, CALLEE)
// ------ Caller Corectness -------
// ------ Call Site Information -------
-INLINE_OBSERVATION(ARGS_OK, bool, "arguments suitable", INFORMATION, CALLSITE)
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(LOCALS_OK, bool, "locals suitable", 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)
public:
// Factory method for getting policies
- static InlinePolicy* getPolicy(Compiler* compiler);
+ static InlinePolicy* getPolicy(Compiler* compiler, bool isPrejitRoot);
// Obligatory virtual dtor
virtual ~InlinePolicy() {}
InlineObservation getObservation() const { return inlObservation; }
// Policy observations
- virtual void noteCandidate(InlineObservation obs) = 0;
virtual void noteSuccess() = 0;
- virtual void note(InlineObservation obs) = 0;
+ virtual void noteBool(InlineObservation obs, bool value) = 0;
virtual void noteFatal(InlineObservation obs) = 0;
virtual void noteInt(InlineObservation obs, int value) = 0;
virtual void noteDouble(InlineObservation obs, double value) = 0;
protected:
- InlinePolicy()
+ InlinePolicy(bool isPrejitRoot)
: inlDecision(InlineDecision::UNDECIDED)
, inlObservation(InlineObservation::CALLEE_UNUSED_INITIAL)
+ , inlIsPrejitRoot(isPrejitRoot)
{
// empty
}
protected:
- InlineDecision inlDecision;
+ InlineDecision inlDecision;
InlineObservation inlObservation;
+ bool inlIsPrejitRoot;
};
// InlineResult summarizes what is known about the viability of a
return inlDecisionIsDecided(inlPolicy->getDecision());
}
- // noteCandidate indicates the prospective inline has passed at least
- // some of the correctness checks and is still a viable inline
- // candidate, but no decision has been made yet.
- //
- // This may be called multiple times as various tests are performed
- // and the candidate gets closer and closer to actually getting
- // inlined.
- void noteCandidate(InlineObservation obs)
- {
- assert(!isDecided());
-
- // Check the impact, it should be INFORMATION
- InlineImpact impact = inlGetImpact(obs);
- assert(impact == InlineImpact::INFORMATION);
-
- inlPolicy->noteCandidate(obs);
- }
-
// noteSuccess means the all the various checks have passed and
// the inline can happen.
void noteSuccess()
inlPolicy->noteSuccess();
}
- // Make an observation, and update internal state appropriately.
+ // Make a true observation, and update internal state
+ // appropriately.
//
// Caller is expected to call isFailure after this to see whether
// more observation is desired.
void note(InlineObservation obs)
{
- inlPolicy->note(obs);
+ inlPolicy->noteBool(obs, true);
+ }
+
+ // Make a boolean observation, and update internal state
+ // appropriately.
+ //
+ // Caller is expected to call isFailure after this to see whether
+ // more observation is desired.
+ void noteBool(InlineObservation obs, bool value)
+ {
+ inlPolicy->noteBool(obs, value);
}
// Make an observation that must lead to immediate failure.
// getPolicy: Factory method for getting an InlinePolicy
//
// Arguments:
-// compiler - the compiler instance that will evaluate inlines
+// compiler - the compiler instance that will evaluate inlines
+// isPrejitRoot - true if this policy is evaluating a prejit root
//
// Return Value:
// InlinePolicy to use in evaluating the inlines
// Determines which of the various policies should apply,
// and creates (or reuses) a policy instance to use.
-InlinePolicy* InlinePolicy::getPolicy(Compiler* compiler)
+InlinePolicy* InlinePolicy::getPolicy(Compiler* compiler, bool isPrejitRoot)
{
// For now, always create a Legacy policy.
- InlinePolicy* policy = new (compiler, CMK_Inlining) LegacyPolicy(compiler);
+ InlinePolicy* policy = new (compiler, CMK_Inlining) LegacyPolicy(compiler, isPrejitRoot);
return policy;
}
//------------------------------------------------------------------------
-// noteCandidate: handle passing a set of inlining checks successfully
-//
-// Arguments:
-// obs - the current obsevation
-
-void LegacyPolicy::noteCandidate(InlineObservation obs)
-{
- assert(!inlDecisionIsDecided(inlDecision));
-
- // Check the impact, it should be INFORMATION
- InlineImpact impact = inlGetImpact(obs);
- assert(impact == InlineImpact::INFORMATION);
-
- switch (inlDecision)
- {
- case InlineDecision::UNDECIDED:
- case InlineDecision::CANDIDATE:
- // Candidate observations overwrite one another
- inlDecision = InlineDecision::CANDIDATE;
- inlObservation = obs;
- break;
- default:
- // SUCCESS or NEVER or FAILURE or ??
- assert(!"Unexpected inlDecision");
- unreached();
- }
-
- // Now fall through to the general handling.
- note(obs);
-}
-
-//------------------------------------------------------------------------
// noteSuccess: handle finishing all the inlining checks successfully
void LegacyPolicy::noteSuccess()
//
// Arguments:
// obs - the current obsevation
+// value - the value of the observation
-void LegacyPolicy::note(InlineObservation obs)
+void LegacyPolicy::noteBool(InlineObservation obs, bool value)
{
// Check the impact
InlineImpact impact = inlGetImpact(obs);
switch (obs)
{
case InlineObservation::CALLEE_IS_FORCE_INLINE:
- inlIsForceInline = true;
+ // We may make the force-inline observation more than
+ // once. All observations should agree.
+ assert(!inlIsForceInlineKnown || (inlIsForceInline == value));
+ inlIsForceInline = value;
+ inlIsForceInlineKnown = true;
break;
case InlineObservation::CALLEE_IS_INSTANCE_CTOR:
- inlIsInstanceCtor = true;
+ inlIsInstanceCtor = value;
break;
case InlineObservation::CALLEE_CLASS_PROMOTABLE:
- inlIsFromPromotableValueClass = true;
+ inlIsFromPromotableValueClass = value;
break;
case InlineObservation::CALLEE_HAS_SIMD:
- inlHasSimd = true;
+ inlHasSimd = value;
break;
case InlineObservation::CALLEE_LOOKS_LIKE_WRAPPER:
- inlLooksLikeWrapperMethod = true;
+ inlLooksLikeWrapperMethod = value;
break;
case InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST:
- inlArgFeedsConstantTest = true;
+ inlArgFeedsConstantTest = value;
break;
case InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK:
- inlArgFeedsRangeCheck = true;
+ inlArgFeedsRangeCheck = value;
break;
case InlineObservation::CALLEE_IS_MOSTLY_LOAD_STORE:
- inlMethodIsMostlyLoadStore = true;
+ inlMethodIsMostlyLoadStore = value;
break;
case InlineObservation::CALLEE_HAS_SWITCH:
// Pass this one on, it should cause inlining to fail.
propagate = true;
break;
case InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST:
- inlConstantFeedsConstantTest = true;
+ inlConstantFeedsConstantTest = value;
+ break;
+ case InlineObservation::CALLSITE_NATIVE_SIZE_ESTIMATE_OK:
+ // Passed the profitability screen. Update candidacy.
+ setCandidate(obs);
break;
default:
// Ignore the remainder for now
{
case InlineObservation::CALLEE_MAXSTACK:
{
+ assert(inlIsForceInlineKnown);
+
unsigned calleeMaxStack = static_cast<unsigned>(value);
if (!inlIsForceInline && (calleeMaxStack > SMALL_STACK_SIZE))
case InlineObservation::CALLEE_NUMBER_OF_BASIC_BLOCKS:
{
+ assert(inlIsForceInlineKnown);
assert(value != 0);
unsigned basicBlockCount = static_cast<unsigned>(value);
break;
}
- case InlineObservation::CALLEE_NUMBER_OF_IL_BYTES:
+ case InlineObservation::CALLEE_IL_CODE_SIZE:
{
+ assert(inlIsForceInlineKnown);
assert(value != 0);
-
unsigned ilByteSize = static_cast<unsigned>(value);
- if (!inlIsForceInline && (ilByteSize > inlCompiler->getImpInlineSize()))
+ // Now that we know size and forceinline state,
+ // update candidacy.
+ if (ilByteSize <= ALWAYS_INLINE_SIZE)
{
+ // Candidate based on small size
+ setCandidate(InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE);
+ }
+ else if (inlIsForceInline)
+ {
+ // Candidate based on force inline
+ setCandidate(InlineObservation::CALLEE_IS_FORCE_INLINE);
+ }
+ else if (ilByteSize <= inlCompiler->getImpInlineSize())
+ {
+ // Candidate, pending profitability evaluation
+ setCandidate(InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
+ }
+ else
+ {
+ // Callee too big, not a candidate
setNever(InlineObservation::CALLEE_TOO_MUCH_IL);
}
default:
// Ignore all other information
- note(obs);
break;
}
}
void LegacyPolicy::noteDouble(InlineObservation obs, double value)
{
+ // Ignore for now...
(void) value;
- note(obs);
+ (void) obs;
}
//------------------------------------------------------------------------
switch (inlDecision)
{
case InlineDecision::FAILURE:
- // Repeated failure only ok if in prejit scan
- assert(isPrejitScan());
+ // Repeated failure only ok if evaluating a prejit root
+ assert(inlIsPrejitRoot);
break;
case InlineDecision::UNDECIDED:
case InlineDecision::CANDIDATE:
switch (inlDecision)
{
case InlineDecision::NEVER:
- // Repeated never only ok if in prejit scan
- assert(isPrejitScan());
+ // Repeated never only ok if evaluating a prejit root
+ assert(inlIsPrejitRoot);
break;
case InlineDecision::UNDECIDED:
case InlineDecision::CANDIDATE:
}
//------------------------------------------------------------------------
+// setCandidate: helper updating candidacy
+//
+// Arguments:
+// obs - the current obsevation
+//
+// Note:
+// Candidate observations are handled here. If the inline has already
+// failed, they're ignored. If there's already a candidate reason,
+// this new reason trumps it.
+
+void LegacyPolicy::setCandidate(InlineObservation obs)
+{
+ // Ignore if this inline is going to fail.
+ if (inlDecisionIsFailure(inlDecision))
+ {
+ return;
+ }
+
+ // We should not have declared success yet.
+ assert(!inlDecisionIsSuccess(inlDecision));
+
+ // Update, overriding any previous candidacy.
+ inlDecision = InlineDecision::CANDIDATE;
+ inlObservation = obs;
+}
+
+//------------------------------------------------------------------------
// determineMultiplier: determine benefit multiplier for this inline
//
// Notes: uses the accumulated set of observations to compute a
public:
// Construct a LegacyPolicy
- LegacyPolicy(Compiler* compiler)
- : InlinePolicy()
+ LegacyPolicy(Compiler* compiler, bool isPrejitRoot)
+ : InlinePolicy(isPrejitRoot)
, inlCompiler(compiler)
, inlIsForceInline(false)
+ , inlIsForceInlineKnown(false)
, inlIsInstanceCtor(false)
, inlIsFromPromotableValueClass(false)
, inlHasSimd(false)
}
// Policy observations
- void noteCandidate(InlineObservation obs) override;
void noteSuccess() override;
- void note(InlineObservation obs) override;
void noteFatal(InlineObservation obs) override;
+ void noteBool(InlineObservation obs, bool value) override;
void noteInt(InlineObservation obs, int value) override;
void noteDouble(InlineObservation obs, double value) override;
// Helper methods
void noteInternal(InlineObservation obs);
+ void setCandidate(InlineObservation obs);
void setFailure(InlineObservation obs);
void setNever(InlineObservation obs);
- // True if this policy is being used to scan a method during
- // prejitting.
- bool isPrejitScan() const
- {
- return !inlCompiler->compIsForInlining();
- }
-
// Constants
const unsigned MAX_BASIC_BLOCKS = 5;
// Data members
Compiler* inlCompiler;
bool inlIsForceInline :1;
+ bool inlIsForceInlineKnown :1;
bool inlIsInstanceCtor :1;
bool inlIsFromPromotableValueClass :1;
bool inlHasSimd :1;