blob: c2bc940830a71ffc32ed940728b4cb87d5f889d9 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "Backend.h"
InliningThreshold::InliningThreshold(uint nonLoadByteCodeCount, bool forLoopBody, bool aggressive) : nonLoadByteCodeCount(nonLoadByteCodeCount)
{
this->forLoopBody = forLoopBody;
if (aggressive)
{
SetAggressiveHeuristics();
}
else
{
SetHeuristics();
}
}
void InliningThreshold::SetAggressiveHeuristics()
{
Assert(!this->forLoopBody);
int limit = CONFIG_FLAG(AggressiveInlineThreshold);
inlineThreshold = limit;
constructorInlineThreshold = limit;
outsideLoopInlineThreshold = limit;
leafInlineThreshold = limit;
loopInlineThreshold = limit;
polymorphicInlineThreshold = limit;
maxNumberOfInlineesWithLoop = CONFIG_FLAG(MaxNumberOfInlineesWithLoop);
inlineCountMax = CONFIG_FLAG(AggressiveInlineCountMax);
}
void InliningThreshold::Reset()
{
SetHeuristics();
}
void InliningThreshold::SetHeuristics()
{
inlineThreshold = CONFIG_FLAG(InlineThreshold);
// Inline less aggressively in large functions since the register pressure is likely high.
// Small functions shouldn't be a problem.
if (nonLoadByteCodeCount > 800)
{
inlineThreshold -= CONFIG_FLAG(InlineThresholdAdjustCountInLargeFunction);
}
else if (nonLoadByteCodeCount > 200)
{
inlineThreshold -= CONFIG_FLAG(InlineThresholdAdjustCountInMediumSizedFunction);
}
else if (nonLoadByteCodeCount < 50)
{
inlineThreshold += CONFIG_FLAG(InlineThresholdAdjustCountInSmallFunction);
}
constructorInlineThreshold = CONFIG_FLAG(ConstructorInlineThreshold);
outsideLoopInlineThreshold = CONFIG_FLAG(OutsideLoopInlineThreshold);
leafInlineThreshold = CONFIG_FLAG(LeafInlineThreshold);
loopInlineThreshold = CONFIG_FLAG(LoopInlineThreshold);
polymorphicInlineThreshold = CONFIG_FLAG(PolymorphicInlineThreshold);
maxNumberOfInlineesWithLoop = CONFIG_FLAG(MaxNumberOfInlineesWithLoop);
constantArgumentInlineThreshold = CONFIG_FLAG(ConstantArgumentInlineThreshold);
inlineCountMax = !forLoopBody ? CONFIG_FLAG(InlineCountMax) : CONFIG_FLAG(InlineCountMaxInLoopBodies);
}
// Called from background thread to commit inlining.
bool InliningHeuristics::BackendInlineIntoInliner(const FunctionJITTimeInfo * inlinee,
Func * inliner,
Func *topFunction,
Js::ProfileId callSiteId,
bool isConstructorCall,
bool isFixedMethodCall, // Reserved
bool isCallOutsideLoopInTopFunc, // There is a loop for sure and this call is outside loop
bool isCallInsideLoop,
uint recursiveInlineDepth,
uint16 constantArguments
)
{
// We have one piece of additional data in backend, whether we are outside loop or inside
// This function decides to inline or not based on that additional data. Most of the filtering is already done by DeciderInlineIntoInliner which is called
// during work item creation.
// This is additional filtering during actual inlining phase.
// Note *order* is important
// Following are
// 1. Constructor is always inlined (irrespective of inside or outside)
// 2. If the inlinee candidate has constant argument and that argument is used for a branch and the inlinee size is within ConstantArgumentInlineThreshold(157) we inline
// 3. Inside loops:
// 3a. Leaf function will always get inlined (irrespective of leaf has loop or not)
// 3b. If the inlinee has loops, don't inline it (Basically avoiding inlining a loop within another loop unless its leaf).
// 4. Outside loop (inliner has loops):
// 4a. Only inline small inlinees. Governed by OutsideLoopInlineThreshold (16)
// 5. Rest are inlined.
#if ENABLE_DEBUG_CONFIG_OPTIONS
char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif
// TODO: OOP JIT, somehow need to track across functions
bool doBackEndAggressiveInline = (constantArguments & inlinee->GetBody()->GetArgUsedForBranch()) != 0;
if (!PHASE_OFF(Js::InlineRecursivePhase, inliner)
&& inlinee->GetBody()->GetAddr() == inliner->GetJITFunctionBody()->GetAddr()
&& (!inlinee->GetBody()->CanInlineRecursively(recursiveInlineDepth, doBackEndAggressiveInline)))
{
INLINE_TESTTRACE(_u("INLINING: Skip Inline (backend): Recursive inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s) Depth: %d\n"),
inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3),
recursiveInlineDepth);
return false;
}
if(inlinee->IsJsBuiltInForceInline() ||
PHASE_FORCE(Js::InlinePhase, this->topFunc) ||
PHASE_FORCE(Js::InlinePhase, inliner) ||
PHASE_FORCE(Js::InlinePhase, inlinee))
{
return true;
}
if (PHASE_FORCE(Js::InlineTreePhase, this->topFunc) ||
PHASE_FORCE(Js::InlineTreePhase, inliner))
{
return true;
}
if (PHASE_FORCE(Js::InlineAtEveryCallerPhase, inlinee))
{
return true;
}
const JITTimeProfileInfo *dynamicProfile = inliner->GetReadOnlyProfileInfo();
bool doConstantArgumentInlining = (dynamicProfile && dynamicProfile->GetConstantArgInfo(callSiteId) & inlinee->GetBody()->GetArgUsedForBranch()) != 0;
if (doConstantArgumentInlining && inlinee->GetBody()->GetNonLoadByteCodeCount() < (uint)threshold.constantArgumentInlineThreshold)
{
return true;
}
if (topFunction->GetWorkItem()->GetJITTimeInfo()->IsAggressiveInliningEnabled())
{
return true;
}
if (isConstructorCall)
{
return true;
}
if (isCallInsideLoop && IsInlineeLeaf(inlinee))
{
return true;
}
if (isCallInsideLoop && inlinee->GetBody()->HasLoops() ) // Don't inline function with loops inside another loop unless it is a leaf
{
INLINE_TESTTRACE(_u("INLINING: Skip Inline (backend): Recursive loop inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s)\n"),
inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
return false;
}
byte scale = 1;
if (doBackEndAggressiveInline)
{
scale = 2;
}
if (isCallOutsideLoopInTopFunc &&
(threshold.outsideLoopInlineThreshold < 0 ||
inlinee->GetBody()->GetNonLoadByteCodeCount() > (uint)threshold.outsideLoopInlineThreshold * scale))
{
Assert(!isCallInsideLoop);
INLINE_TESTTRACE(_u("INLINING: Skip Inline (backend): Inlining outside loop doesn't meet OutsideLoopInlineThreshold: %d \tBytecode size: %d\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s)\n"),
threshold.outsideLoopInlineThreshold,
inlinee->GetBody()->GetByteCodeCount(),
inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
return false;
}
return true;
}