CodeGenWorkItem.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. // TODO: (michhol) OOP JIT put bodyData directly on function body rather than doing this copying
  29. // bytecode
  30. this->jitData.bodyData.byteCodeLength = functionBody->GetByteCode()->GetLength();
  31. this->jitData.bodyData.byteCodeBuffer = functionBody->GetByteCode()->GetBuffer();
  32. // const table
  33. this->jitData.bodyData.constCount = functionBody->GetConstantCount();
  34. if (functionBody->GetConstantCount() > 0)
  35. {
  36. // TODO (michhol): OOP JIT, will be different for asm.js
  37. this->jitData.bodyData.constTable = (intptr_t *)functionBody->GetConstTable();
  38. this->jitData.bodyData.constTypeTable = HeapNewArray(int32, functionBody->GetConstantCount());
  39. for (Js::RegSlot reg = Js::FunctionBody::FirstRegSlot; reg < functionBody->GetConstantCount(); ++reg)
  40. {
  41. Js::Var varConst = functionBody->GetConstantVar(reg);
  42. Assert(varConst != nullptr);
  43. if (Js::TaggedInt::Is(varConst) ||
  44. varConst == (Js::Var)&Js::NullFrameDisplay ||
  45. varConst == (Js::Var)&Js::StrictNullFrameDisplay)
  46. {
  47. // don't need TypeId for these
  48. jitData.bodyData.constTypeTable[reg - Js::FunctionBody::FirstRegSlot] = Js::TypeId::TypeIds_Limit;
  49. }
  50. else
  51. {
  52. jitData.bodyData.constTypeTable[reg - Js::FunctionBody::FirstRegSlot] = Js::JavascriptOperators::GetTypeId(varConst);
  53. }
  54. }
  55. }
  56. // statement map
  57. Js::SmallSpanSequence * statementMap = functionBody->GetStatementMapSpanSequence();
  58. this->jitData.bodyData.statementMap.baseValue = statementMap->baseValue;
  59. if (statementMap->pActualOffsetList)
  60. {
  61. this->jitData.bodyData.statementMap.actualOffsetLength = statementMap->pActualOffsetList->GetLength();
  62. this->jitData.bodyData.statementMap.actualOffsetList = statementMap->pActualOffsetList->GetBuffer();
  63. }
  64. else
  65. {
  66. this->jitData.bodyData.statementMap.actualOffsetLength = 0;
  67. this->jitData.bodyData.statementMap.actualOffsetList = nullptr;
  68. }
  69. if (statementMap->pStatementBuffer)
  70. {
  71. this->jitData.bodyData.statementMap.statementLength = statementMap->pStatementBuffer->GetLength();
  72. this->jitData.bodyData.statementMap.statementBuffer = statementMap->pStatementBuffer->GetBuffer();
  73. }
  74. else
  75. {
  76. this->jitData.bodyData.statementMap.statementLength = 0;
  77. this->jitData.bodyData.statementMap.statementBuffer = nullptr;
  78. }
  79. this->jitData.bodyData.inlineCacheCount = functionBody->GetInlineCacheCount();
  80. if (functionBody->GetInlineCacheCount() > 0)
  81. {
  82. this->jitData.bodyData.cacheIdToPropertyIdMap = functionBody->GetCacheIdToPropertyIdMap();
  83. this->jitData.bodyData.inlineCaches = reinterpret_cast<intptr_t*>(functionBody->GetInlineCaches());
  84. }
  85. // body data
  86. this->jitData.bodyData.functionBodyAddr = (intptr_t)functionBody;
  87. this->jitData.bodyData.funcNumber = functionBody->GetFunctionNumber();
  88. this->jitData.bodyData.localFuncId = functionBody->GetLocalFunctionId();
  89. this->jitData.bodyData.sourceContextId = functionBody->GetSourceContextId();
  90. this->jitData.bodyData.nestedCount = functionBody->GetNestedCount();
  91. this->jitData.bodyData.scopeSlotArraySize = functionBody->scopeSlotArraySize;
  92. this->jitData.bodyData.attributes = functionBody->GetAttributes();
  93. if (functionBody->GetUtf8SourceInfo()->GetCbLength() > UINT_MAX)
  94. {
  95. Js::Throw::OutOfMemory();
  96. }
  97. this->jitData.bodyData.byteCodeCount = functionBody->GetByteCodeCount();
  98. this->jitData.bodyData.byteCodeInLoopCount = functionBody->GetByteCodeInLoopCount();
  99. this->jitData.bodyData.nonLoadByteCodeCount = functionBody->GetByteCodeWithoutLDACount();
  100. this->jitData.bodyData.loopCount = functionBody->GetLoopCount();
  101. this->jitData.bodyData.localFrameDisplayReg = functionBody->GetLocalFrameDisplayReg();
  102. this->jitData.bodyData.localClosureReg = functionBody->GetLocalClosureReg();
  103. this->jitData.bodyData.envReg = functionBody->GetEnvReg();
  104. this->jitData.bodyData.firstTmpReg = functionBody->GetFirstTmpReg();
  105. this->jitData.bodyData.varCount = functionBody->GetVarCount();
  106. this->jitData.bodyData.innerScopeCount = functionBody->GetInnerScopeCount();
  107. if (functionBody->GetInnerScopeCount() > 0)
  108. {
  109. this->jitData.bodyData.firstInnerScopeReg = functionBody->FirstInnerScopeReg();
  110. }
  111. this->jitData.bodyData.envDepth = functionBody->GetEnvDepth();
  112. this->jitData.bodyData.profiledIterations = functionBody->GetProfiledIterations();
  113. this->jitData.bodyData.profiledCallSiteCount = functionBody->GetProfiledCallSiteCount();
  114. this->jitData.bodyData.inParamCount = functionBody->GetInParamsCount();
  115. this->jitData.bodyData.thisRegisterForEventHandler = functionBody->GetThisRegForEventHandler();
  116. this->jitData.bodyData.funcExprScopeRegister = functionBody->GetFuncExprScopeReg();
  117. this->jitData.bodyData.flags = functionBody->GetFlags();
  118. this->jitData.bodyData.doBackendArgumentsOptimization = functionBody->GetDoBackendArgumentsOptimization();
  119. this->jitData.bodyData.isLibraryCode = functionBody->GetUtf8SourceInfo()->GetIsLibraryCode();
  120. this->jitData.bodyData.isAsmJsMode = functionBody->GetIsAsmjsMode();
  121. this->jitData.bodyData.isStrictMode = functionBody->GetIsStrictMode();
  122. this->jitData.bodyData.isGlobalFunc = functionBody->GetIsGlobalFunc();
  123. this->jitData.bodyData.doJITLoopBody = functionBody->DoJITLoopBody();
  124. this->jitData.bodyData.hasScopeObject = functionBody->HasScopeObject();
  125. this->jitData.bodyData.hasImplicitArgIns = functionBody->GetHasImplicitArgIns();
  126. this->jitData.bodyData.hasCachedScopePropIds = functionBody->HasCachedScopePropIds();
  127. this->jitData.bodyData.inlineCachesOnFunctionObject = functionBody->GetInlineCachesOnFunctionObject();
  128. this->jitData.bodyData.doInterruptProbe = functionBody->GetScriptContext()->GetThreadContext()->DoInterruptProbe(functionBody);
  129. // work item data
  130. this->jitData.type = type;
  131. this->jitData.isJitInDebugMode = isJitInDebugMode;
  132. ResetJitMode();
  133. this->jitData.loopNumber = GetLoopNumber();
  134. }
  135. CodeGenWorkItem::~CodeGenWorkItem()
  136. {
  137. if(queuedFullJitWorkItem)
  138. {
  139. HeapDelete(queuedFullJitWorkItem);
  140. }
  141. }
  142. //
  143. // Helps determine whether a function should be speculatively jitted.
  144. // This function is only used once and is used in a time-critical area, so
  145. // be careful with it (moving it around actually caused around a 5% perf
  146. // regression on a test).
  147. //
  148. bool CodeGenWorkItem::ShouldSpeculativelyJit(uint byteCodeSizeGenerated) const
  149. {
  150. if(PHASE_OFF(Js::FullJitPhase, this->functionBody))
  151. {
  152. return false;
  153. }
  154. byteCodeSizeGenerated += this->GetByteCodeCount();
  155. if(CONFIG_FLAG(ProfileBasedSpeculativeJit))
  156. {
  157. Assert(!CONFIG_ISENABLED(Js::NoDynamicProfileInMemoryCacheFlag));
  158. // JIT this now if we are under the speculation cap.
  159. return
  160. byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap) ||
  161. (
  162. byteCodeSizeGenerated < (uint)CONFIG_FLAG(ProfileBasedSpeculationCap) &&
  163. this->ShouldSpeculativelyJitBasedOnProfile()
  164. );
  165. }
  166. else
  167. {
  168. return byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap);
  169. }
  170. }
  171. bool CodeGenWorkItem::ShouldSpeculativelyJitBasedOnProfile() const
  172. {
  173. Js::FunctionBody* functionBody = this->GetFunctionBody();
  174. uint loopPercentage = (functionBody->GetByteCodeInLoopCount()*100) / (functionBody->GetByteCodeCount() + 1);
  175. uint straightLineSize = functionBody->GetByteCodeCount() - functionBody->GetByteCodeInLoopCount();
  176. // This ensures only small and loopy functions are prejitted.
  177. if(loopPercentage >= 50 || straightLineSize < 300)
  178. {
  179. Js::SourceDynamicProfileManager* profileManager = functionBody->GetSourceContextInfo()->sourceDynamicProfileManager;
  180. if(profileManager != nullptr)
  181. {
  182. functionBody->SetIsSpeculativeJitCandidate();
  183. if(!functionBody->HasDynamicProfileInfo())
  184. {
  185. return false;
  186. }
  187. Js::ExecutionFlags executionFlags = profileManager->IsFunctionExecuted(functionBody->GetLocalFunctionId());
  188. if(executionFlags == Js::ExecutionFlags_Executed)
  189. {
  190. return true;
  191. }
  192. }
  193. }
  194. return false;
  195. }
  196. /*
  197. A comment about how to cause certain phases to only be on:
  198. INT = Interpreted, SJ = SimpleJit, FJ = FullJit
  199. To get only the following levels on, use the flags:
  200. INT: -noNative
  201. SJ : -forceNative -off:fullJit
  202. FJ : -forceNative -off:simpleJit
  203. INT, SJ: -off:fullJit
  204. INT, FJ: -off:simpleJit
  205. SJ, FG: -forceNative
  206. INT, SJ, FG: (default)
  207. */
  208. void CodeGenWorkItem::OnAddToJitQueue()
  209. {
  210. Assert(!this->isInJitQueue);
  211. this->isInJitQueue = true;
  212. VerifyJitMode();
  213. this->entryPointInfo->SetCodeGenQueued();
  214. if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_QUEUED()))
  215. {
  216. WCHAR displayNameBuffer[256];
  217. WCHAR* displayName = displayNameBuffer;
  218. size_t sizeInChars = this->GetDisplayName(displayName, 256);
  219. if(sizeInChars > 256)
  220. {
  221. displayName = HeapNewArray(WCHAR, sizeInChars);
  222. this->GetDisplayName(displayName, 256);
  223. }
  224. JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_QUEUED(
  225. this->GetFunctionNumber(),
  226. displayName,
  227. this->GetScriptContext(),
  228. this->GetInterpretedCount()));
  229. if(displayName != displayNameBuffer)
  230. {
  231. HeapDeleteArray(sizeInChars, displayName);
  232. }
  233. }
  234. }
  235. void CodeGenWorkItem::OnRemoveFromJitQueue(NativeCodeGenerator* generator)
  236. {
  237. // This is called from within the lock
  238. this->isInJitQueue = false;
  239. this->entryPointInfo->SetCodeGenPending();
  240. functionBody->GetScriptContext()->GetThreadContext()->UnregisterCodeGenRecyclableData(this->recyclableData);
  241. this->recyclableData = nullptr;
  242. if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_DEQUEUED()))
  243. {
  244. WCHAR displayNameBuffer[256];
  245. WCHAR* displayName = displayNameBuffer;
  246. size_t sizeInChars = this->GetDisplayName(displayName, 256);
  247. if(sizeInChars > 256)
  248. {
  249. displayName = HeapNewArray(WCHAR, sizeInChars);
  250. this->GetDisplayName(displayName, 256);
  251. }
  252. JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_DEQUEUED(
  253. this->GetFunctionNumber(),
  254. displayName,
  255. this->GetScriptContext(),
  256. this->GetInterpretedCount()));
  257. if(displayName != displayNameBuffer)
  258. {
  259. HeapDeleteArray(sizeInChars, displayName);
  260. }
  261. }
  262. if(this->Type() == JsLoopBodyWorkItemType)
  263. {
  264. // Go ahead and delete it and let it re-queue if more interpreting of the loop happens
  265. auto loopBodyWorkItem = static_cast<JsLoopBodyCodeGen*>(this);
  266. loopBodyWorkItem->loopHeader->ResetInterpreterCount();
  267. loopBodyWorkItem->GetEntryPoint()->Reset();
  268. HeapDelete(loopBodyWorkItem);
  269. }
  270. else
  271. {
  272. Assert(GetJitMode() == ExecutionMode::FullJit); // simple JIT work items are not removed from the queue
  273. GetFunctionBody()->OnFullJitDequeued(static_cast<Js::FunctionEntryPointInfo *>(GetEntryPoint()));
  274. // Add it back to the list of available functions to be jitted
  275. generator->AddWorkItem(this);
  276. }
  277. }
  278. void CodeGenWorkItem::OnWorkItemProcessFail(NativeCodeGenerator* codeGen)
  279. {
  280. if (!isAllocationCommitted && this->allocation != nullptr && this->allocation->allocation != nullptr)
  281. {
  282. #if DBG
  283. this->allocation->allocation->isNotExecutableBecauseOOM = true;
  284. #endif
  285. codeGen->FreeNativeCodeGenAllocation(this->allocation->allocation->address);
  286. }
  287. }
  288. void CodeGenWorkItem::FinalizeNativeCode(Func *func)
  289. {
  290. NativeCodeData * data = func->GetNativeCodeDataAllocator()->Finalize();
  291. NativeCodeData * transferData = func->GetTransferDataAllocator()->Finalize();
  292. CodeGenNumberChunk * numberChunks = func->GetNumberAllocator()->Finalize();
  293. this->functionBody->RecordNativeBaseAddress((BYTE *)GetCodeAddress(), GetCodeSize(), data, transferData, numberChunks, GetEntryPoint(), GetLoopNumber());
  294. func->GetEmitBufferManager()->CompletePreviousAllocation(this->GetAllocation());
  295. }
  296. QueuedFullJitWorkItem *CodeGenWorkItem::GetQueuedFullJitWorkItem() const
  297. {
  298. return queuedFullJitWorkItem;
  299. }
  300. QueuedFullJitWorkItem *CodeGenWorkItem::EnsureQueuedFullJitWorkItem()
  301. {
  302. if(queuedFullJitWorkItem)
  303. {
  304. return queuedFullJitWorkItem;
  305. }
  306. queuedFullJitWorkItem = HeapNewNoThrow(QueuedFullJitWorkItem, this);
  307. return queuedFullJitWorkItem;
  308. }