InliningHeuristics.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 aggressive) : nonLoadByteCodeCount(nonLoadByteCodeCount)
  7. {
  8. this->forLoopBody = forLoopBody;
  9. if (aggressive)
  10. {
  11. SetAggressiveHeuristics();
  12. }
  13. else
  14. {
  15. SetHeuristics();
  16. }
  17. }
  18. void InliningThreshold::SetAggressiveHeuristics()
  19. {
  20. Assert(!this->forLoopBody);
  21. int limit = CONFIG_FLAG(AggressiveInlineThreshold);
  22. inlineThreshold = limit;
  23. constructorInlineThreshold = limit;
  24. outsideLoopInlineThreshold = limit;
  25. leafInlineThreshold = limit;
  26. loopInlineThreshold = limit;
  27. polymorphicInlineThreshold = limit;
  28. maxNumberOfInlineesWithLoop = CONFIG_FLAG(MaxNumberOfInlineesWithLoop);
  29. inlineCountMax = CONFIG_FLAG(AggressiveInlineCountMax);
  30. }
  31. void InliningThreshold::Reset()
  32. {
  33. SetHeuristics();
  34. }
  35. void InliningThreshold::SetHeuristics()
  36. {
  37. inlineThreshold = CONFIG_FLAG(InlineThreshold);
  38. // Inline less aggressively in large functions since the register pressure is likely high.
  39. // Small functions shouldn't be a problem.
  40. if (nonLoadByteCodeCount > 800)
  41. {
  42. inlineThreshold -= CONFIG_FLAG(InlineThresholdAdjustCountInLargeFunction);
  43. }
  44. else if (nonLoadByteCodeCount > 200)
  45. {
  46. inlineThreshold -= CONFIG_FLAG(InlineThresholdAdjustCountInMediumSizedFunction);
  47. }
  48. else if (nonLoadByteCodeCount < 50)
  49. {
  50. inlineThreshold += CONFIG_FLAG(InlineThresholdAdjustCountInSmallFunction);
  51. }
  52. constructorInlineThreshold = CONFIG_FLAG(ConstructorInlineThreshold);
  53. outsideLoopInlineThreshold = CONFIG_FLAG(OutsideLoopInlineThreshold);
  54. leafInlineThreshold = CONFIG_FLAG(LeafInlineThreshold);
  55. loopInlineThreshold = CONFIG_FLAG(LoopInlineThreshold);
  56. polymorphicInlineThreshold = CONFIG_FLAG(PolymorphicInlineThreshold);
  57. maxNumberOfInlineesWithLoop = CONFIG_FLAG(MaxNumberOfInlineesWithLoop);
  58. constantArgumentInlineThreshold = CONFIG_FLAG(ConstantArgumentInlineThreshold);
  59. inlineCountMax = !forLoopBody ? CONFIG_FLAG(InlineCountMax) : CONFIG_FLAG(InlineCountMaxInLoopBodies);
  60. }
  61. // Called from background thread to commit inlining.
  62. bool InliningHeuristics::BackendInlineIntoInliner(const FunctionJITTimeInfo * inlinee,
  63. Func * inliner,
  64. Func *topFunction,
  65. Js::ProfileId callSiteId,
  66. bool isConstructorCall,
  67. bool isFixedMethodCall, // Reserved
  68. bool isCallOutsideLoopInTopFunc, // There is a loop for sure and this call is outside loop
  69. bool isCallInsideLoop,
  70. uint recursiveInlineDepth,
  71. uint16 constantArguments
  72. )
  73. {
  74. // We have one piece of additional data in backend, whether we are outside loop or inside
  75. // This function decides to inline or not based on that additional data. Most of the filtering is already done by DeciderInlineIntoInliner which is called
  76. // during work item creation.
  77. // This is additional filtering during actual inlining phase.
  78. // Note *order* is important
  79. // Following are
  80. // 1. Constructor is always inlined (irrespective of inside or outside)
  81. // 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
  82. // 3. Inside loops:
  83. // 3a. Leaf function will always get inlined (irrespective of leaf has loop or not)
  84. // 3b. If the inlinee has loops, don't inline it (Basically avoiding inlining a loop within another loop unless its leaf).
  85. // 4. Outside loop (inliner has loops):
  86. // 4a. Only inline small inlinees. Governed by OutsideLoopInlineThreshold (16)
  87. // 5. Rest are inlined.
  88. #if ENABLE_DEBUG_CONFIG_OPTIONS
  89. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  90. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  91. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  92. #endif
  93. // TODO: OOP JIT, somehow need to track across functions
  94. bool doBackEndAggressiveInline = (constantArguments & inlinee->GetBody()->GetArgUsedForBranch()) != 0;
  95. if (!PHASE_OFF(Js::InlineRecursivePhase, inliner)
  96. && inlinee->GetBody()->GetAddr() == inliner->GetJITFunctionBody()->GetAddr()
  97. && (!inlinee->GetBody()->CanInlineRecursively(recursiveInlineDepth, doBackEndAggressiveInline)))
  98. {
  99. INLINE_TESTTRACE(_u("INLINING: Skip Inline (backend): Recursive inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s) Depth: %d\n"),
  100. inlinee->GetBody()->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  101. inliner->GetJITFunctionBody()->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  102. topFunc->GetBody()->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3),
  103. recursiveInlineDepth);
  104. return false;
  105. }
  106. if(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. }