//
// If we're inlining we also look at the argument values supplied by
// the caller at this call site.
+//
+// The crude stack model may overestimate stack depth.
void Compiler::fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, bool isInlining)
{
// We should be able to record inline observations.
assert(compInlineResult != nullptr);
- if (!stack.IsStackTwoDeep())
+ // The stack only has to be 1 deep for BRTRUE/FALSE
+ bool lookForBranchCases = stack.IsStackAtLeastOneDeep();
+
+ if (compInlineResult->UsesLegacyPolicy())
{
- // The stack only has to be 1 deep for BRTRUE/FALSE
- if (stack.IsStackOneDeep())
+ // LegacyPolicy misses cases where the stack is really one
+ // deep but the model says it's two deep. We need to do
+ // likewise to preseve old behavior.
+ lookForBranchCases &= !stack.IsStackTwoDeep();
+ }
+
+ if (lookForBranchCases)
+ {
+ if (opcode == CEE_BRFALSE || opcode == CEE_BRFALSE_S ||
+ opcode == CEE_BRTRUE || opcode == CEE_BRTRUE_S)
{
- if (opcode == CEE_BRFALSE || opcode == CEE_BRFALSE_S ||
- opcode == CEE_BRTRUE || opcode == CEE_BRTRUE_S)
+ unsigned slot0 = stack.GetSlot0();
+ if (FgStack::IsArgument(slot0))
{
- unsigned slot0 = stack.GetSlot0();
- if (FgStack::IsArgument(slot0))
- {
- compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST);
+ compInlineResult->Note(InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST);
- if (isInlining)
+ if (isInlining)
+ {
+ // Check for the double whammy of an incoming constant argument
+ // feeding a constant test.
+ unsigned varNum = FgStack::SlotTypeToArgNum(slot0);
+ if (impInlineInfo->inlArgInfo[varNum].argNode->OperIsConst())
{
- // Check for the double whammy of an incoming constant argument
- // feeding a constant test.
- unsigned varNum = FgStack::SlotTypeToArgNum(slot0);
- if (impInlineInfo->inlArgInfo[varNum].argNode->OperIsConst())
- {
- compInlineResult->Note(InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST);
- }
+ compInlineResult->Note(InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST);
}
}
}
+
+ return;
}
+ }
+ // Remaining cases require at least two things on the stack.
+ if (!stack.IsStackTwoDeep())
+ {
return;
}
// Policy policies
virtual bool PropagateNeverToRuntime() const = 0;
+ virtual bool IsLegacyPolicy() const = 0;
// Policy estimates
virtual int CodeSizeEstimate() = 0;
return m_Policy;
}
+ // True if the policy used for this result is (exactly) the legacy
+ // policy.
+ bool UsesLegacyPolicy() const
+ {
+ return m_Policy->IsLegacyPolicy();
+ }
+
// SetReported indicates that this particular result doesn't need
// to be reported back to the runtime, either because the runtime
// already knows, or we aren't actually inlining yet.
// LegacyPolicy ignores this for prejit roots.
if (!m_IsPrejitRoot)
{
- m_ArgFeedsConstantTest = value;
+ m_ArgFeedsConstantTest++;
}
break;
// LegacyPolicy ignores this for prejit roots.
if (!m_IsPrejitRoot)
{
- m_ArgFeedsRangeCheck = value;
+ m_ArgFeedsRangeCheck++;
}
break;
// We shouldn't see this for a prejit root since
// we don't know anything about callers.
assert(!m_IsPrejitRoot);
- m_ConstantFeedsConstantTest = value;
+ m_ConstantArgFeedsConstantTest++;
break;
case InlineObservation::CALLEE_BEGIN_OPCODE_SCAN:
JITDUMP("\nInline candidate looks like a wrapper method. Multiplier increased to %g.", multiplier);
}
- if (m_ArgFeedsConstantTest)
+ if (m_ArgFeedsConstantTest > 0)
{
multiplier += 1.0;
JITDUMP("\nInline candidate has an arg that feeds a constant test. Multiplier increased to %g.", multiplier);
JITDUMP("\nInline candidate is mostly loads and stores. Multiplier increased to %g.", multiplier);
}
- if (m_ArgFeedsRangeCheck)
+ if (m_ArgFeedsRangeCheck > 0)
{
multiplier += 0.5;
JITDUMP("\nInline candidate has arg that feeds range check. Multiplier increased to %g.", multiplier);
}
- if (m_ConstantFeedsConstantTest)
+ if (m_ConstantArgFeedsConstantTest > 0)
{
multiplier += 3.0;
JITDUMP("\nInline candidate has const arg that feeds a conditional. Multiplier increased to %g.", multiplier);
break;
case InlineObservation::CALLEE_ARG_FEEDS_CONSTANT_TEST:
- m_ArgFeedsConstantTest = value;
+ assert(value);
+ m_ArgFeedsConstantTest++;
break;
case InlineObservation::CALLEE_ARG_FEEDS_RANGE_CHECK:
- m_ArgFeedsRangeCheck = value;
+ assert(value);
+ m_ArgFeedsRangeCheck++;
+ break;
+
+ case InlineObservation::CALLSITE_CONSTANT_ARG_FEEDS_TEST:
+ assert(value);
+ m_ConstantArgFeedsConstantTest++;
break;
default:
6.021 * m_CallCount +
-0.238 * m_IsInstanceCtor +
-5.357 * m_IsFromPromotableValueClass +
- -7.901 * m_ConstantFeedsConstantTest +
+ -7.901 * (m_ConstantArgFeedsConstantTest > 0 ? 1 : 0) +
0.065 * m_CalleeNativeSizeEstimate;
// Scaled up and reported as an integer value.
fprintf(file, ",ArgFeedsConstantTest");
fprintf(file, ",IsMostlyLoadStore");
fprintf(file, ",ArgFeedsRangeCheck");
- fprintf(file, ",ConstantFeedsConstantTest");
+ fprintf(file, ",ConstantArgFeedsConstantTest");
fprintf(file, ",CalleeNativeSizeEstimate");
fprintf(file, ",CallsiteNativeSizeEstimate");
fprintf(file, ",ModelCodeSizeEstimate");
fprintf(file, ",%u", m_IsFromPromotableValueClass ? 1 : 0);
fprintf(file, ",%u", m_HasSimd ? 1 : 0);
fprintf(file, ",%u", m_LooksLikeWrapperMethod ? 1 : 0);
- fprintf(file, ",%u", m_ArgFeedsConstantTest ? 1 : 0);
+ fprintf(file, ",%u", m_ArgFeedsConstantTest);
fprintf(file, ",%u", m_MethodIsMostlyLoadStore ? 1 : 0);
- fprintf(file, ",%u", m_ArgFeedsRangeCheck ? 1 : 0);
- fprintf(file, ",%u", m_ConstantFeedsConstantTest ? 1 : 0);
+ fprintf(file, ",%u", m_ArgFeedsRangeCheck);
+ fprintf(file, ",%u", m_ConstantArgFeedsConstantTest);
fprintf(file, ",%d", m_CalleeNativeSizeEstimate);
fprintf(file, ",%d", m_CallsiteNativeSizeEstimate);
fprintf(file, ",%d", m_ModelCodeSizeEstimate);
: LegalPolicy(isPrejitRoot)
, m_RootCompiler(compiler)
, m_StateMachine(nullptr)
+ , m_Multiplier(0.0)
, m_CodeSize(0)
, m_CallsiteFrequency(InlineCallsiteFrequency::UNUSED)
, m_InstructionCount(0)
, m_LoadStoreCount(0)
+ , m_ArgFeedsConstantTest(0)
+ , m_ArgFeedsRangeCheck(0)
+ , m_ConstantArgFeedsConstantTest(0)
, m_CalleeNativeSizeEstimate(0)
, m_CallsiteNativeSizeEstimate(0)
- , m_Multiplier(0.0)
, m_IsForceInline(false)
, m_IsForceInlineKnown(false)
, m_IsInstanceCtor(false)
, m_IsFromPromotableValueClass(false)
, m_HasSimd(false)
, m_LooksLikeWrapperMethod(false)
- , m_ArgFeedsConstantTest(false)
, m_MethodIsMostlyLoadStore(false)
- , m_ArgFeedsRangeCheck(false)
- , m_ConstantFeedsConstantTest(false)
{
// empty
}
// Policy policies
bool PropagateNeverToRuntime() const override { return true; }
+ bool IsLegacyPolicy() const override { return true; }
// Policy estimates
int CodeSizeEstimate() override;
// Data members
Compiler* m_RootCompiler; // root compiler instance
CodeSeqSM* m_StateMachine;
+ double m_Multiplier;
unsigned m_CodeSize;
InlineCallsiteFrequency m_CallsiteFrequency;
unsigned m_InstructionCount;
unsigned m_LoadStoreCount;
+ unsigned m_ArgFeedsConstantTest;
+ unsigned m_ArgFeedsRangeCheck;
+ unsigned m_ConstantArgFeedsConstantTest;
int m_CalleeNativeSizeEstimate;
int m_CallsiteNativeSizeEstimate;
- double m_Multiplier;
bool m_IsForceInline :1;
bool m_IsForceInlineKnown :1;
bool m_IsInstanceCtor :1;
bool m_IsFromPromotableValueClass :1;
bool m_HasSimd :1;
bool m_LooksLikeWrapperMethod :1;
- bool m_ArgFeedsConstantTest :1;
bool m_MethodIsMostlyLoadStore :1;
- bool m_ArgFeedsRangeCheck :1;
- bool m_ConstantFeedsConstantTest :1;
};
#ifdef DEBUG
// Policy policies
bool PropagateNeverToRuntime() const override { return true; }
+ bool IsLegacyPolicy() const override { return false; }
// Policy estimates
int CodeSizeEstimate() override
// DiscretionaryPolicy is a variant of the legacy policy. It differs
// in that there is no ALWAYS_INLINE class, there is no IL size limit,
-// and in prejit mode, discretionary failures do not set the "NEVER"
-// inline bit.
+// it does not try and maintain legacy compatabilty, and in prejit mode,
+// discretionary failures do not set the "NEVER" inline bit.
//
// It is useful for gathering data about inline costs.
// Policy policies
bool PropagateNeverToRuntime() const override;
+ bool IsLegacyPolicy() const override { return false; }
// Policy determinations
void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;