InliningHeuristics.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. InliningThreshold::InliningThreshold(uint nonLoadByteCodeCount, bool forLoopBody, bool asmjs) :
  7. nonLoadByteCodeCount(nonLoadByteCodeCount),
  8. forLoopBody(forLoopBody),
  9. asmjs(asmjs)
  10. {
  11. SetHeuristics();
  12. }
  13. void InliningThreshold::SetAggressiveHeuristics()
  14. {
  15. Assert(!this->forLoopBody);
  16. int limit = CONFIG_FLAG(AggressiveInlineThreshold);
  17. inlineThreshold = limit;
  18. constructorInlineThreshold = limit;
  19. outsideLoopInlineThreshold = limit;
  20. leafInlineThreshold = limit;
  21. loopInlineThreshold = limit;
  22. polymorphicInlineThreshold = limit;
  23. maxNumberOfInlineesWithLoop = CONFIG_FLAG(MaxNumberOfInlineesWithLoop);
  24. inlineCountMax = CONFIG_FLAG(AggressiveInlineCountMax);
  25. }
  26. void InliningThreshold::Reset()
  27. {
  28. SetHeuristics();
  29. }
  30. void InliningThreshold::SetHeuristics()
  31. {
  32. inlineThreshold = CONFIG_FLAG(InlineThreshold);
  33. // Inline less aggressively in large functions since the register pressure is likely high.
  34. // Small functions shouldn't be a problem.
  35. if (nonLoadByteCodeCount > 800)
  36. {
  37. inlineThreshold -= CONFIG_FLAG(InlineThresholdAdjustCountInLargeFunction);
  38. }
  39. else if (nonLoadByteCodeCount > 200)
  40. {
  41. inlineThreshold -= CONFIG_FLAG(InlineThresholdAdjustCountInMediumSizedFunction);
  42. }
  43. else if (nonLoadByteCodeCount < 50)
  44. {
  45. inlineThreshold += CONFIG_FLAG(InlineThresholdAdjustCountInSmallFunction);
  46. }
  47. if (this->asmjs)
  48. {
  49. inlineThreshold += CONFIG_FLAG(AsmJsInlineAdjust);
  50. }
  51. constructorInlineThreshold = CONFIG_FLAG(ConstructorInlineThreshold);
  52. outsideLoopInlineThreshold = CONFIG_FLAG(OutsideLoopInlineThreshold);
  53. leafInlineThreshold = CONFIG_FLAG(LeafInlineThreshold);
  54. loopInlineThreshold = CONFIG_FLAG(LoopInlineThreshold);
  55. polymorphicInlineThreshold = CONFIG_FLAG(PolymorphicInlineThreshold);
  56. maxNumberOfInlineesWithLoop = CONFIG_FLAG(MaxNumberOfInlineesWithLoop);
  57. constantArgumentInlineThreshold = CONFIG_FLAG(ConstantArgumentInlineThreshold);
  58. inlineCountMax = !forLoopBody ? CONFIG_FLAG(InlineCountMax) : CONFIG_FLAG(InlineCountMaxInLoopBodies);
  59. }
  60. // Called from background thread to commit inlining.
  61. bool InliningHeuristics::BackendInlineIntoInliner(const FunctionJITTimeInfo * inlinee,
  62. Func * inliner,
  63. Func *topFunction,
  64. Js::ProfileId callSiteId,
  65. bool isConstructorCall,
  66. bool isFixedMethodCall, // Reserved
  67. bool isCallOutsideLoopInTopFunc, // There is a loop for sure and this call is outside loop
  68. bool isCallInsideLoop,
  69. uint recursiveInlineDepth,
  70. uint16 constantArguments
  71. )
  72. {
  73. // We have one piece of additional data in backend, whether we are outside loop or inside
  74. // This function decides to inline or not based on that additional data. Most of the filtering is already done by DeciderInlineIntoInliner which is called
  75. // during work item creation.
  76. // This is additional filtering during actual inlining phase.
  77. // Note *order* is important
  78. // Following are
  79. // 1. Constructor is always inlined (irrespective of inside or outside)
  80. // 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
  81. // 3. Inside loops:
  82. // 3a. Leaf function will always get inlined (irrespective of leaf has loop or not)
  83. // 3b. If the inlinee has loops, don't inline it (Basically avoiding inlining a loop within another loop unless its leaf).
  84. // 4. Outside loop (inliner has loops):
  85. // 4a. Only inline small inlinees. Governed by OutsideLoopInlineThreshold (16)
  86. // 5. Rest are inlined.
  87. #if ENABLE_DEBUG_CONFIG_OPTIONS
  88. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  89. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  90. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  91. #endif
  92. // TODO: OOP JIT, somehow need to track across functions
  93. bool doBackEndAggressiveInline = (constantArguments & inlinee->GetBody()->GetArgUsedForBranch()) != 0;
  94. if (!PHASE_OFF(Js::InlineRecursivePhase, inliner)
  95. && inlinee->GetBody()->GetAddr() == inliner->GetJITFunctionBody()->GetAddr()
  96. && (!inlinee->GetBody()->CanInlineRecursively(recursiveInlineDepth, doBackEndAggressiveInline)))
  97. {
  98. INLINE_TESTTRACE(_u("INLINING: Skip Inline (backend): Recursive inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s) Depth: %d\n"),
  99. inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  100. inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  101. topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3),
  102. recursiveInlineDepth);
  103. return false;
  104. }
  105. if(inlinee->IsJsBuiltInForceInline() ||
  106. PHASE_FORCE(Js::InlinePhase, this->topFunc) ||
  107. PHASE_FORCE(Js::InlinePhase, inliner) ||
  108. PHASE_FORCE(Js::InlinePhase, inlinee))
  109. {
  110. return true;
  111. }
  112. if (PHASE_FORCE(Js::InlineTreePhase, this->topFunc) ||
  113. PHASE_FORCE(Js::InlineTreePhase, inliner))
  114. {
  115. return true;
  116. }
  117. if (PHASE_FORCE(Js::InlineAtEveryCallerPhase, inlinee))
  118. {
  119. return true;
  120. }
  121. const JITTimeProfileInfo *dynamicProfile = inliner->GetReadOnlyProfileInfo();
  122. bool doConstantArgumentInlining = (dynamicProfile && dynamicProfile->GetConstantArgInfo(callSiteId) & inlinee->GetBody()->GetArgUsedForBranch()) != 0;
  123. if (doConstantArgumentInlining && inlinee->GetBody()->GetNonLoadByteCodeCount() < (uint)threshold.constantArgumentInlineThreshold)
  124. {
  125. return true;
  126. }
  127. if (topFunction->GetWorkItem()->GetJITTimeInfo()->IsAggressiveInliningEnabled())
  128. {
  129. return true;
  130. }
  131. if (isConstructorCall)
  132. {
  133. return true;
  134. }
  135. if (isCallInsideLoop && IsInlineeLeaf(inlinee))
  136. {
  137. return true;
  138. }
  139. if (isCallInsideLoop && inlinee->GetBody()->HasLoops() ) // Don't inline function with loops inside another loop unless it is a leaf
  140. {
  141. INLINE_TESTTRACE(_u("INLINING: Skip Inline (backend): Recursive loop inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s)\n"),
  142. inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  143. inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  144. topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
  145. return false;
  146. }
  147. byte scale = 1;
  148. if (doBackEndAggressiveInline)
  149. {
  150. scale = 2;
  151. }
  152. if (isCallOutsideLoopInTopFunc &&
  153. (threshold.outsideLoopInlineThreshold < 0 ||
  154. inlinee->GetBody()->GetNonLoadByteCodeCount() > (uint)threshold.outsideLoopInlineThreshold * scale))
  155. {
  156. Assert(!isCallInsideLoop);
  157. 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"),
  158. threshold.outsideLoopInlineThreshold,
  159. inlinee->GetBody()->GetByteCodeCount(),
  160. inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  161. inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  162. topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
  163. return false;
  164. }
  165. return true;
  166. }