CodeGenWorkItem.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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. #include "Language/SourceDynamicProfileManager.h"
  7. CodeGenWorkItem::CodeGenWorkItem(
  8. JsUtil::JobManager *const manager,
  9. Js::FunctionBody *const functionBody,
  10. Js::EntryPointInfo* entryPointInfo,
  11. bool isJitInDebugMode,
  12. CodeGenWorkItemType type)
  13. : JsUtil::Job(manager)
  14. , functionBody(functionBody)
  15. , entryPointInfo(entryPointInfo)
  16. , recyclableData(nullptr)
  17. , isInJitQueue(false)
  18. , isAllocationCommitted(false)
  19. , queuedFullJitWorkItem(nullptr)
  20. , allocation(nullptr)
  21. #ifdef IR_VIEWER
  22. , isRejitIRViewerFunction(false)
  23. , irViewerOutput(nullptr)
  24. , irViewerRequestContext(nullptr)
  25. #endif
  26. {
  27. this->jitData = {0};
  28. // work item data
  29. this->jitData.type = type;
  30. this->jitData.isJitInDebugMode = isJitInDebugMode;
  31. ResetJitMode();
  32. }
  33. CodeGenWorkItem::~CodeGenWorkItem()
  34. {
  35. if(queuedFullJitWorkItem)
  36. {
  37. HeapDelete(queuedFullJitWorkItem);
  38. }
  39. }
  40. //
  41. // Helps determine whether a function should be speculatively jitted.
  42. // This function is only used once and is used in a time-critical area, so
  43. // be careful with it (moving it around actually caused around a 5% perf
  44. // regression on a test).
  45. //
  46. bool CodeGenWorkItem::ShouldSpeculativelyJit(uint byteCodeSizeGenerated) const
  47. {
  48. if(PHASE_OFF(Js::FullJitPhase, this->functionBody))
  49. {
  50. return false;
  51. }
  52. byteCodeSizeGenerated += this->GetByteCodeCount();
  53. if(CONFIG_FLAG(ProfileBasedSpeculativeJit))
  54. {
  55. Assert(!CONFIG_ISENABLED(Js::NoDynamicProfileInMemoryCacheFlag));
  56. // JIT this now if we are under the speculation cap.
  57. return
  58. byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap) ||
  59. (
  60. byteCodeSizeGenerated < (uint)CONFIG_FLAG(ProfileBasedSpeculationCap) &&
  61. this->ShouldSpeculativelyJitBasedOnProfile()
  62. );
  63. }
  64. else
  65. {
  66. return byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap);
  67. }
  68. }
  69. bool CodeGenWorkItem::ShouldSpeculativelyJitBasedOnProfile() const
  70. {
  71. Js::FunctionBody* functionBody = this->GetFunctionBody();
  72. uint loopPercentage = (functionBody->GetByteCodeInLoopCount()*100) / (functionBody->GetByteCodeCount() + 1);
  73. uint straightLineSize = functionBody->GetByteCodeCount() - functionBody->GetByteCodeInLoopCount();
  74. // This ensures only small and loopy functions are prejitted.
  75. if(loopPercentage >= 50 || straightLineSize < 300)
  76. {
  77. Js::SourceDynamicProfileManager* profileManager = functionBody->GetSourceContextInfo()->sourceDynamicProfileManager;
  78. if(profileManager != nullptr)
  79. {
  80. functionBody->SetIsSpeculativeJitCandidate();
  81. if(!functionBody->HasDynamicProfileInfo())
  82. {
  83. return false;
  84. }
  85. Js::ExecutionFlags executionFlags = profileManager->IsFunctionExecuted(functionBody->GetLocalFunctionId());
  86. if(executionFlags == Js::ExecutionFlags_Executed)
  87. {
  88. return true;
  89. }
  90. }
  91. }
  92. return false;
  93. }
  94. /*
  95. A comment about how to cause certain phases to only be on:
  96. INT = Interpreted, SJ = SimpleJit, FJ = FullJit
  97. To get only the following levels on, use the flags:
  98. INT: -noNative
  99. SJ : -forceNative -off:fullJit
  100. FJ : -forceNative -off:simpleJit
  101. INT, SJ: -off:fullJit
  102. INT, FJ: -off:simpleJit
  103. SJ, FG: -forceNative
  104. INT, SJ, FG: (default)
  105. */
  106. void CodeGenWorkItem::OnAddToJitQueue()
  107. {
  108. Assert(!this->isInJitQueue);
  109. this->isInJitQueue = true;
  110. VerifyJitMode();
  111. this->entryPointInfo->SetCodeGenQueued();
  112. if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_QUEUED()))
  113. {
  114. WCHAR displayNameBuffer[256];
  115. WCHAR* displayName = displayNameBuffer;
  116. size_t sizeInChars = this->GetDisplayName(displayName, 256);
  117. if(sizeInChars > 256)
  118. {
  119. displayName = HeapNewArray(WCHAR, sizeInChars);
  120. this->GetDisplayName(displayName, 256);
  121. }
  122. JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_QUEUED(
  123. this->GetFunctionNumber(),
  124. displayName,
  125. this->GetScriptContext(),
  126. this->GetInterpretedCount()));
  127. if(displayName != displayNameBuffer)
  128. {
  129. HeapDeleteArray(sizeInChars, displayName);
  130. }
  131. }
  132. }
  133. void CodeGenWorkItem::OnRemoveFromJitQueue(NativeCodeGenerator* generator)
  134. {
  135. // This is called from within the lock
  136. this->isInJitQueue = false;
  137. this->entryPointInfo->SetCodeGenPending();
  138. functionBody->GetScriptContext()->GetThreadContext()->UnregisterCodeGenRecyclableData(this->recyclableData);
  139. this->recyclableData = nullptr;
  140. if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_DEQUEUED()))
  141. {
  142. WCHAR displayNameBuffer[256];
  143. WCHAR* displayName = displayNameBuffer;
  144. size_t sizeInChars = this->GetDisplayName(displayName, 256);
  145. if(sizeInChars > 256)
  146. {
  147. displayName = HeapNewArray(WCHAR, sizeInChars);
  148. this->GetDisplayName(displayName, 256);
  149. }
  150. JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_DEQUEUED(
  151. this->GetFunctionNumber(),
  152. displayName,
  153. this->GetScriptContext(),
  154. this->GetInterpretedCount()));
  155. if(displayName != displayNameBuffer)
  156. {
  157. HeapDeleteArray(sizeInChars, displayName);
  158. }
  159. }
  160. if(this->Type() == JsLoopBodyWorkItemType)
  161. {
  162. // Go ahead and delete it and let it re-queue if more interpreting of the loop happens
  163. auto loopBodyWorkItem = static_cast<JsLoopBodyCodeGen*>(this);
  164. loopBodyWorkItem->loopHeader->ResetInterpreterCount();
  165. loopBodyWorkItem->GetEntryPoint()->Reset();
  166. HeapDelete(loopBodyWorkItem);
  167. }
  168. else
  169. {
  170. Assert(GetJitMode() == ExecutionMode::FullJit); // simple JIT work items are not removed from the queue
  171. GetFunctionBody()->OnFullJitDequeued(static_cast<Js::FunctionEntryPointInfo *>(GetEntryPoint()));
  172. // Add it back to the list of available functions to be jitted
  173. generator->AddWorkItem(this);
  174. }
  175. }
  176. void CodeGenWorkItem::OnWorkItemProcessFail(NativeCodeGenerator* codeGen)
  177. {
  178. if (!isAllocationCommitted && this->allocation != nullptr && this->allocation->allocation != nullptr)
  179. {
  180. #if DBG
  181. this->allocation->allocation->isNotExecutableBecauseOOM = true;
  182. #endif
  183. codeGen->FreeNativeCodeGenAllocation(this->allocation->allocation->address, nullptr);
  184. }
  185. }
  186. QueuedFullJitWorkItem *CodeGenWorkItem::GetQueuedFullJitWorkItem() const
  187. {
  188. return queuedFullJitWorkItem;
  189. }
  190. QueuedFullJitWorkItem *CodeGenWorkItem::EnsureQueuedFullJitWorkItem()
  191. {
  192. if(queuedFullJitWorkItem)
  193. {
  194. return queuedFullJitWorkItem;
  195. }
  196. queuedFullJitWorkItem = HeapNewNoThrow(QueuedFullJitWorkItem, this);
  197. return queuedFullJitWorkItem;
  198. }