2
0

CodeGenWorkItem.cpp 7.1 KB

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