| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- //-------------------------------------------------------------------------------------------------------
- // 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 asmjs) :
- nonLoadByteCodeCount(nonLoadByteCodeCount),
- forLoopBody(forLoopBody),
- asmjs(asmjs)
- {
- 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);
- }
- if (this->asmjs)
- {
- inlineThreshold += CONFIG_FLAG(AsmJsInlineAdjust);
- }
- 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;
- }
|