| //------------------------------------------------------------------------------------------------------- |
| // 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; |
| } |