InliningDecider.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  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. InliningDecider::InliningDecider(Js::FunctionBody *const topFunc, bool isLoopBody, bool isInDebugMode, const ExecutionMode jitMode)
  7. : topFunc(topFunc), isLoopBody(isLoopBody), isInDebugMode(isInDebugMode), jitMode(jitMode), bytecodeInlinedCount(0), numberOfInlineesWithLoop(0), threshold(topFunc->GetByteCodeWithoutLDACount(), isLoopBody, topFunc->GetIsAsmjsMode())
  8. {
  9. Assert(topFunc);
  10. }
  11. InliningDecider::~InliningDecider()
  12. {
  13. INLINE_FLUSH();
  14. }
  15. bool InliningDecider::InlineIntoTopFunc() const
  16. {
  17. if (this->jitMode == ExecutionMode::SimpleJit ||
  18. PHASE_OFF(Js::InlinePhase, this->topFunc) ||
  19. PHASE_OFF(Js::GlobOptPhase, this->topFunc))
  20. {
  21. return false;
  22. }
  23. #ifdef _M_IX86
  24. if (this->topFunc->GetHasTry())
  25. {
  26. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  27. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  28. #endif
  29. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has try\tCaller: %s (%s)\n"), this->topFunc->GetDisplayName(),
  30. this->topFunc->GetDebugNumberSet(debugStringBuffer));
  31. // Glob opt doesn't run on function with try, so we can't generate bailout for it
  32. return false;
  33. }
  34. #endif
  35. return InlineIntoInliner(topFunc);
  36. }
  37. bool InliningDecider::InlineIntoInliner(Js::FunctionBody *const inliner) const
  38. {
  39. Assert(inliner);
  40. Assert(this->jitMode == ExecutionMode::FullJit);
  41. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  42. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  43. #endif
  44. if (PHASE_OFF(Js::InlinePhase, inliner) ||
  45. PHASE_OFF(Js::GlobOptPhase, inliner))
  46. {
  47. return false;
  48. }
  49. if (!inliner->HasDynamicProfileInfo())
  50. {
  51. INLINE_TESTTRACE(_u("INLINING: Skip Inline: No dynamic profile info\tCaller: %s (%s)\n"), inliner->GetDisplayName(),
  52. inliner->GetDebugNumberSet(debugStringBuffer));
  53. return false;
  54. }
  55. if (inliner->GetProfiledCallSiteCount() == 0 && !inliner->GetAnyDynamicProfileInfo()->HasLdFldCallSiteInfo())
  56. {
  57. INLINE_TESTTRACE_VERBOSE(_u("INLINING: Skip Inline: Leaf function\tCaller: %s (%s)\n"), inliner->GetDisplayName(),
  58. inliner->GetDebugNumberSet(debugStringBuffer));
  59. // Nothing to do
  60. return false;
  61. }
  62. if (!inliner->GetAnyDynamicProfileInfo()->HasCallSiteInfo(inliner))
  63. {
  64. INLINE_TESTTRACE(_u("INLINING: Skip Inline: No call site info\tCaller: %s (#%d)\n"), inliner->GetDisplayName(),
  65. inliner->GetDebugNumberSet(debugStringBuffer));
  66. return false;
  67. }
  68. return true;
  69. }
  70. Js::FunctionInfo *InliningDecider::GetCallSiteFuncInfo(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId, bool* isConstructorCall, bool* isPolymorphicCall)
  71. {
  72. Assert(inliner);
  73. Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
  74. const auto profileData = inliner->GetAnyDynamicProfileInfo();
  75. Assert(profileData);
  76. return profileData->GetCallSiteInfo(inliner, profiledCallSiteId, isConstructorCall, isPolymorphicCall);
  77. }
  78. Js::FunctionInfo * InliningDecider::GetCallSiteCallbackInfo(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId)
  79. {
  80. Assert(inliner != nullptr);
  81. Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
  82. Js::DynamicProfileInfo * profileData = inliner->GetAnyDynamicProfileInfo();
  83. Assert(profileData != nullptr);
  84. return profileData->GetCallbackInfo(inliner, profiledCallSiteId);
  85. }
  86. Js::FunctionInfo * InliningDecider::GetCallApplyTargetInfo(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId)
  87. {
  88. Assert(inliner != nullptr);
  89. Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
  90. Js::DynamicProfileInfo * profileData = inliner->GetAnyDynamicProfileInfo();
  91. Assert(profileData != nullptr);
  92. return profileData->GetCallApplyTargetInfo(inliner, profiledCallSiteId);
  93. }
  94. uint16 InliningDecider::GetConstantArgInfo(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId)
  95. {
  96. Assert(inliner);
  97. Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
  98. const auto profileData = inliner->GetAnyDynamicProfileInfo();
  99. Assert(profileData);
  100. return profileData->GetConstantArgInfo(profiledCallSiteId);
  101. }
  102. bool InliningDecider::HasCallSiteInfo(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId)
  103. {
  104. Assert(inliner);
  105. Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
  106. const auto profileData = inliner->GetAnyDynamicProfileInfo();
  107. Assert(profileData);
  108. return profileData->HasCallSiteInfo(inliner, profiledCallSiteId);
  109. }
  110. Js::FunctionInfo *InliningDecider::InlineCallSite(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId, uint recursiveInlineDepth)
  111. {
  112. bool isConstructorCall;
  113. bool isPolymorphicCall;
  114. Js::FunctionInfo *functionInfo = GetCallSiteFuncInfo(inliner, profiledCallSiteId, &isConstructorCall, &isPolymorphicCall);
  115. if (functionInfo)
  116. {
  117. return Inline(inliner, functionInfo, isConstructorCall, false, false, GetConstantArgInfo(inliner, profiledCallSiteId), profiledCallSiteId, recursiveInlineDepth, true);
  118. }
  119. return nullptr;
  120. }
  121. Js::FunctionInfo * InliningDecider::InlineCallback(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId, uint recursiveInlineDepth)
  122. {
  123. Js::FunctionInfo * functionInfo = GetCallSiteCallbackInfo(inliner, profiledCallSiteId);
  124. if (functionInfo)
  125. {
  126. return Inline(inliner, functionInfo, false, false, true, GetConstantArgInfo(inliner, profiledCallSiteId), profiledCallSiteId, recursiveInlineDepth, true);
  127. }
  128. return nullptr;
  129. }
  130. Js::FunctionInfo * InliningDecider::InlineCallApplyTarget(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId, uint recursiveInlineDepth)
  131. {
  132. Js::FunctionInfo * functionInfo = GetCallApplyTargetInfo(inliner, profiledCallSiteId);
  133. if (functionInfo)
  134. {
  135. return Inline(inliner, functionInfo, false, false, false, GetConstantArgInfo(inliner, profiledCallSiteId), profiledCallSiteId, recursiveInlineDepth, true);
  136. }
  137. return functionInfo;
  138. }
  139. uint InliningDecider::InlinePolymorphicCallSite(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId,
  140. Js::FunctionBody** functionBodyArray, uint functionBodyArrayLength, bool* canInlineArray, uint recursiveInlineDepth)
  141. {
  142. Assert(inliner);
  143. Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
  144. Assert(functionBodyArray);
  145. const auto profileData = inliner->GetAnyDynamicProfileInfo();
  146. Assert(profileData);
  147. bool isConstructorCall;
  148. if (!profileData->GetPolymorphicCallSiteInfo(inliner, profiledCallSiteId, &isConstructorCall, functionBodyArray, functionBodyArrayLength))
  149. {
  150. return 0;
  151. }
  152. uint inlineeCount = 0;
  153. uint actualInlineeCount = 0;
  154. for (inlineeCount = 0; inlineeCount < functionBodyArrayLength; inlineeCount++)
  155. {
  156. if (!functionBodyArray[inlineeCount])
  157. {
  158. AssertMsg(inlineeCount >= 2, "There are at least two polymorphic call site");
  159. break;
  160. }
  161. if (Inline(inliner, functionBodyArray[inlineeCount]->GetFunctionInfo(), isConstructorCall, true /*isPolymorphicCall*/, false /*isCallback*/, 0, profiledCallSiteId, recursiveInlineDepth, false))
  162. {
  163. canInlineArray[inlineeCount] = true;
  164. actualInlineeCount++;
  165. }
  166. }
  167. if (inlineeCount != actualInlineeCount)
  168. {
  169. // We generate polymorphic dispatch and call even if there are no inlinees as it's seen to provide a perf boost
  170. // Skip loop bodies for now as we do not handle re-jit scenarios for the bailouts from them
  171. if (!PHASE_OFF(Js::PartialPolymorphicInlinePhase, inliner) && !this->isLoopBody)
  172. {
  173. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  174. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  175. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  176. #endif
  177. INLINE_TESTTRACE(_u("Partial inlining of polymorphic call: %s (%s)\tCaller: %s (%s)\n"),
  178. functionBodyArray[inlineeCount - 1]->GetDisplayName(), functionBodyArray[inlineeCount - 1]->GetDebugNumberSet(debugStringBuffer),
  179. inliner->GetDisplayName(),
  180. inliner->GetDebugNumberSet(debugStringBuffer2));
  181. }
  182. else
  183. {
  184. return 0;
  185. }
  186. }
  187. return inlineeCount;
  188. }
  189. Js::FunctionInfo *InliningDecider::Inline(Js::FunctionBody *const inliner,
  190. Js::FunctionInfo* functionInfo,
  191. bool isConstructorCall,
  192. bool isPolymorphicCall,
  193. bool isCallback,
  194. uint16 constantArgInfo,
  195. Js::ProfileId callSiteId,
  196. uint recursiveInlineDepth,
  197. bool allowRecursiveInlining)
  198. {
  199. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  200. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  201. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  202. #endif
  203. Js::FunctionProxy * proxy = functionInfo->GetFunctionProxy();
  204. if (proxy && proxy->IsFunctionBody())
  205. {
  206. if (isLoopBody && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->topFunc))
  207. {
  208. INLINE_TESTTRACE_VERBOSE(_u("INLINING: Skip Inline: Jit loop body: %s (%s)\n"), this->topFunc->GetDisplayName(),
  209. this->topFunc->GetDebugNumberSet(debugStringBuffer));
  210. return nullptr;
  211. }
  212. // Note: disable inline for debugger, as we can't bailout at return from function.
  213. // Alternative can be generate this bailout as part of inline, which can be done later as perf improvement.
  214. const auto inlinee = proxy->GetFunctionBody();
  215. Assert(this->jitMode == ExecutionMode::FullJit);
  216. if (PHASE_OFF(Js::InlinePhase, inlinee) ||
  217. PHASE_OFF(Js::GlobOptPhase, inlinee) ||
  218. !ContinueInliningUserDefinedFunctions(this->bytecodeInlinedCount) ||
  219. this->isInDebugMode)
  220. {
  221. return nullptr;
  222. }
  223. if (functionInfo->IsDeferred() || inlinee->GetByteCode() == nullptr)
  224. {
  225. // DeferredParse...
  226. INLINE_TESTTRACE(_u("INLINING: Skip Inline: No bytecode\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
  227. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
  228. inliner->GetDebugNumberSet(debugStringBuffer2));
  229. return nullptr;
  230. }
  231. #ifdef _M_IX86
  232. if (inlinee->GetHasTry())
  233. {
  234. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has try\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
  235. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
  236. inliner->GetDebugNumberSet(debugStringBuffer2));
  237. return nullptr;
  238. }
  239. #endif
  240. // This is a hard limit as the argOuts array is statically sized.
  241. if (inlinee->GetInParamsCount() > Js::InlineeCallInfo::MaxInlineeArgoutCount)
  242. {
  243. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count greater then MaxInlineeArgoutCount\tInlinee: %s (%s)\tParamcount: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"),
  244. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(), Js::InlineeCallInfo::MaxInlineeArgoutCount,
  245. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2));
  246. return nullptr;
  247. }
  248. // Wasm functions can have no params
  249. if (inlinee->GetInParamsCount() == 0 && !inlinee->GetIsAsmjsMode())
  250. {
  251. // Inline candidate has no params, not even a this pointer. This can only be the global function,
  252. // which we shouldn't inline.
  253. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count is zero!\tInlinee: %s (%s)\tParamcount: %d\tCaller: %s (%s)\n"),
  254. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(),
  255. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2));
  256. return nullptr;
  257. }
  258. if (inlinee->GetDontInline())
  259. {
  260. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Do not inline\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
  261. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
  262. inliner->GetDebugNumberSet(debugStringBuffer2));
  263. return nullptr;
  264. }
  265. // Do not inline a call to a class constructor if it isn't part of a new expression since the call will throw a TypeError anyway.
  266. if (inlinee->IsClassConstructor() && !isConstructorCall)
  267. {
  268. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Class constructor without new keyword\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
  269. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
  270. inliner->GetDebugNumberSet(debugStringBuffer2));
  271. return nullptr;
  272. }
  273. if (!DeciderInlineIntoInliner(inlinee, inliner, isConstructorCall, isPolymorphicCall, constantArgInfo, recursiveInlineDepth, allowRecursiveInlining))
  274. {
  275. return nullptr;
  276. }
  277. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  278. TraceInlining(inliner, inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetByteCodeCount(), this->topFunc, this->bytecodeInlinedCount, inlinee, callSiteId, this->isLoopBody, isCallback);
  279. #endif
  280. this->bytecodeInlinedCount += inlinee->GetByteCodeCount();
  281. return inlinee->GetFunctionInfo();
  282. }
  283. Js::OpCode builtInInlineCandidateOpCode;
  284. ValueType builtInReturnType;
  285. GetBuiltInInfo(functionInfo, &builtInInlineCandidateOpCode, &builtInReturnType);
  286. if(builtInInlineCandidateOpCode == 0 && builtInReturnType.IsUninitialized())
  287. {
  288. return nullptr;
  289. }
  290. Assert(this->jitMode == ExecutionMode::FullJit);
  291. if (builtInInlineCandidateOpCode != 0 &&
  292. (
  293. PHASE_OFF(Js::InlinePhase, inliner) ||
  294. PHASE_OFF(Js::GlobOptPhase, inliner) ||
  295. isConstructorCall
  296. ))
  297. {
  298. return nullptr;
  299. }
  300. // Note: for built-ins at this time we don't have enough data (the instr) to decide whether it's going to be inlined.
  301. return functionInfo;
  302. }
  303. // TODO OOP JIT: add FunctionInfo interface so we can combine these?
  304. /* static */
  305. bool InliningDecider::GetBuiltInInfo(
  306. const FunctionJITTimeInfo *const funcInfo,
  307. Js::OpCode *const inlineCandidateOpCode,
  308. ValueType *const returnType
  309. )
  310. {
  311. Assert(funcInfo);
  312. Assert(inlineCandidateOpCode);
  313. Assert(returnType);
  314. *inlineCandidateOpCode = (Js::OpCode)0;
  315. *returnType = ValueType::Uninitialized;
  316. if (funcInfo->HasBody())
  317. {
  318. return false;
  319. }
  320. return InliningDecider::GetBuiltInInfoCommon(
  321. funcInfo->GetLocalFunctionId(),
  322. inlineCandidateOpCode,
  323. returnType);
  324. }
  325. /* static */
  326. bool InliningDecider::GetBuiltInInfo(
  327. Js::FunctionInfo *const funcInfo,
  328. Js::OpCode *const inlineCandidateOpCode,
  329. ValueType *const returnType
  330. )
  331. {
  332. Assert(funcInfo);
  333. Assert(inlineCandidateOpCode);
  334. Assert(returnType);
  335. *inlineCandidateOpCode = (Js::OpCode)0;
  336. *returnType = ValueType::Uninitialized;
  337. if (funcInfo->HasBody())
  338. {
  339. return false;
  340. }
  341. return InliningDecider::GetBuiltInInfoCommon(
  342. funcInfo->GetLocalFunctionId(),
  343. inlineCandidateOpCode,
  344. returnType);
  345. }
  346. bool InliningDecider::GetBuiltInInfoCommon(
  347. uint localFuncId,
  348. Js::OpCode *const inlineCandidateOpCode,
  349. ValueType *const returnType
  350. )
  351. {
  352. // TODO: consider adding another column to JavascriptBuiltInFunctionList.h/LibraryFunction.h
  353. // and getting helper method from there instead of multiple switch labels. And for return value types too.
  354. switch (localFuncId)
  355. {
  356. case Js::JavascriptBuiltInFunction::Math_Abs:
  357. *inlineCandidateOpCode = Js::OpCode::InlineMathAbs;
  358. break;
  359. case Js::JavascriptBuiltInFunction::Math_Acos:
  360. *inlineCandidateOpCode = Js::OpCode::InlineMathAcos;
  361. break;
  362. case Js::JavascriptBuiltInFunction::Math_Asin:
  363. *inlineCandidateOpCode = Js::OpCode::InlineMathAsin;
  364. break;
  365. case Js::JavascriptBuiltInFunction::Math_Atan:
  366. *inlineCandidateOpCode = Js::OpCode::InlineMathAtan;
  367. break;
  368. case Js::JavascriptBuiltInFunction::Math_Atan2:
  369. *inlineCandidateOpCode = Js::OpCode::InlineMathAtan2;
  370. break;
  371. case Js::JavascriptBuiltInFunction::Math_Cos:
  372. *inlineCandidateOpCode = Js::OpCode::InlineMathCos;
  373. break;
  374. case Js::JavascriptBuiltInFunction::Math_Exp:
  375. *inlineCandidateOpCode = Js::OpCode::InlineMathExp;
  376. break;
  377. case Js::JavascriptBuiltInFunction::Math_Log:
  378. *inlineCandidateOpCode = Js::OpCode::InlineMathLog;
  379. break;
  380. case Js::JavascriptBuiltInFunction::Math_Pow:
  381. *inlineCandidateOpCode = Js::OpCode::InlineMathPow;
  382. break;
  383. case Js::JavascriptBuiltInFunction::Math_Sin:
  384. *inlineCandidateOpCode = Js::OpCode::InlineMathSin;
  385. break;
  386. case Js::JavascriptBuiltInFunction::Math_Sqrt:
  387. *inlineCandidateOpCode = Js::OpCode::InlineMathSqrt;
  388. break;
  389. case Js::JavascriptBuiltInFunction::Math_Tan:
  390. *inlineCandidateOpCode = Js::OpCode::InlineMathTan;
  391. break;
  392. case Js::JavascriptBuiltInFunction::Math_Floor:
  393. *inlineCandidateOpCode = Js::OpCode::InlineMathFloor;
  394. break;
  395. case Js::JavascriptBuiltInFunction::Math_Ceil:
  396. *inlineCandidateOpCode = Js::OpCode::InlineMathCeil;
  397. break;
  398. case Js::JavascriptBuiltInFunction::Math_Round:
  399. *inlineCandidateOpCode = Js::OpCode::InlineMathRound;
  400. break;
  401. case Js::JavascriptBuiltInFunction::Math_Min:
  402. *inlineCandidateOpCode = Js::OpCode::InlineMathMin;
  403. break;
  404. case Js::JavascriptBuiltInFunction::Math_Max:
  405. *inlineCandidateOpCode = Js::OpCode::InlineMathMax;
  406. break;
  407. case Js::JavascriptBuiltInFunction::Math_Imul:
  408. *inlineCandidateOpCode = Js::OpCode::InlineMathImul;
  409. break;
  410. case Js::JavascriptBuiltInFunction::Math_Clz32:
  411. *inlineCandidateOpCode = Js::OpCode::InlineMathClz;
  412. break;
  413. case Js::JavascriptBuiltInFunction::Math_Random:
  414. *inlineCandidateOpCode = Js::OpCode::InlineMathRandom;
  415. break;
  416. case Js::JavascriptBuiltInFunction::Math_Fround:
  417. *inlineCandidateOpCode = Js::OpCode::InlineMathFround;
  418. *returnType = ValueType::Float;
  419. break;
  420. case Js::JavascriptBuiltInFunction::JavascriptArray_Push:
  421. *inlineCandidateOpCode = Js::OpCode::InlineArrayPush;
  422. break;
  423. case Js::JavascriptBuiltInFunction::JavascriptArray_Pop:
  424. *inlineCandidateOpCode = Js::OpCode::InlineArrayPop;
  425. break;
  426. case Js::JavascriptBuiltInFunction::JavascriptArray_Concat:
  427. case Js::JavascriptBuiltInFunction::JavascriptArray_Reverse:
  428. case Js::JavascriptBuiltInFunction::JavascriptArray_Shift:
  429. case Js::JavascriptBuiltInFunction::JavascriptArray_Slice:
  430. case Js::JavascriptBuiltInFunction::JavascriptArray_Splice:
  431. case Js::JavascriptBuiltInFunction::JavascriptString_Link:
  432. goto CallDirectCommon;
  433. case Js::JavascriptBuiltInFunction::JavascriptArray_Join:
  434. case Js::JavascriptBuiltInFunction::JavascriptString_CharAt:
  435. case Js::JavascriptBuiltInFunction::JavascriptString_Concat:
  436. case Js::JavascriptBuiltInFunction::JavascriptString_FromCharCode:
  437. case Js::JavascriptBuiltInFunction::JavascriptString_FromCodePoint:
  438. case Js::JavascriptBuiltInFunction::JavascriptString_Replace:
  439. case Js::JavascriptBuiltInFunction::JavascriptString_Slice:
  440. case Js::JavascriptBuiltInFunction::JavascriptString_Substr:
  441. case Js::JavascriptBuiltInFunction::JavascriptString_Substring:
  442. case Js::JavascriptBuiltInFunction::JavascriptString_ToLocaleLowerCase:
  443. case Js::JavascriptBuiltInFunction::JavascriptString_ToLocaleUpperCase:
  444. case Js::JavascriptBuiltInFunction::JavascriptString_ToLowerCase:
  445. case Js::JavascriptBuiltInFunction::JavascriptString_ToUpperCase:
  446. case Js::JavascriptBuiltInFunction::JavascriptString_Trim:
  447. case Js::JavascriptBuiltInFunction::JavascriptString_TrimLeft:
  448. case Js::JavascriptBuiltInFunction::JavascriptString_TrimRight:
  449. case Js::JavascriptBuiltInFunction::JavascriptString_PadStart:
  450. case Js::JavascriptBuiltInFunction::JavascriptString_PadEnd:
  451. *returnType = ValueType::String;
  452. goto CallDirectCommon;
  453. case Js::JavascriptBuiltInFunction::JavascriptArray_Includes:
  454. case Js::JavascriptBuiltInFunction::JavascriptObject_HasOwnProperty:
  455. case Js::JavascriptBuiltInFunction::JavascriptArray_IsArray:
  456. *returnType = ValueType::Boolean;
  457. goto CallDirectCommon;
  458. case Js::JavascriptBuiltInFunction::JavascriptArray_IndexOf:
  459. case Js::JavascriptBuiltInFunction::JavascriptArray_LastIndexOf:
  460. case Js::JavascriptBuiltInFunction::JavascriptArray_Unshift:
  461. case Js::JavascriptBuiltInFunction::JavascriptString_CharCodeAt:
  462. case Js::JavascriptBuiltInFunction::JavascriptString_IndexOf:
  463. case Js::JavascriptBuiltInFunction::JavascriptString_LastIndexOf:
  464. case Js::JavascriptBuiltInFunction::JavascriptString_Search:
  465. case Js::JavascriptBuiltInFunction::JavascriptRegExp_SymbolSearch:
  466. case Js::JavascriptBuiltInFunction::GlobalObject_ParseInt:
  467. *returnType = ValueType::GetNumberAndLikelyInt(true);
  468. goto CallDirectCommon;
  469. case Js::JavascriptBuiltInFunction::JavascriptString_Split:
  470. *returnType = ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(Js::TypeIds_Array);
  471. goto CallDirectCommon;
  472. case Js::JavascriptBuiltInFunction::JavascriptString_Match:
  473. case Js::JavascriptBuiltInFunction::JavascriptRegExp_Exec:
  474. *returnType =
  475. ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(Js::TypeIds_Array)
  476. .Merge(ValueType::Null);
  477. goto CallDirectCommon;
  478. CallDirectCommon:
  479. *inlineCandidateOpCode = Js::OpCode::CallDirect;
  480. break;
  481. case Js::JavascriptBuiltInFunction::JavascriptFunction_Apply:
  482. *inlineCandidateOpCode = Js::OpCode::InlineFunctionApply;
  483. break;
  484. case Js::JavascriptBuiltInFunction::JavascriptFunction_Call:
  485. *inlineCandidateOpCode = Js::OpCode::InlineFunctionCall;
  486. break;
  487. case Js::JavascriptBuiltInFunction::EngineInterfaceObject_CallInstanceFunction:
  488. *inlineCandidateOpCode = Js::OpCode::InlineCallInstanceFunction;
  489. break;
  490. // The following are not currently inlined, but are tracked for their return type
  491. // TODO: Add more built-ins that return objects. May consider tracking all built-ins.
  492. case Js::JavascriptBuiltInFunction::JavascriptArray_NewInstance:
  493. *returnType = ValueType::GetObject(ObjectType::Array).SetHasNoMissingValues(true).SetArrayTypeId(Js::TypeIds_Array);
  494. break;
  495. case Js::JavascriptBuiltInFunction::Int8Array_NewInstance:
  496. #ifdef _M_X64
  497. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Int8MixedArray) : ValueType::GetObject(ObjectType::Int8Array);
  498. #else
  499. *returnType = ValueType::GetObject(ObjectType::Int8Array);
  500. #endif
  501. break;
  502. case Js::JavascriptBuiltInFunction::Uint8Array_NewInstance:
  503. #ifdef _M_X64
  504. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Uint8MixedArray) : ValueType::GetObject(ObjectType::Uint8Array);
  505. #else
  506. *returnType = ValueType::GetObject(ObjectType::Uint8Array);
  507. #endif
  508. break;
  509. case Js::JavascriptBuiltInFunction::Uint8ClampedArray_NewInstance:
  510. #ifdef _M_X64
  511. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Uint8ClampedMixedArray) : ValueType::GetObject(ObjectType::Uint8ClampedArray);
  512. #else
  513. *returnType = ValueType::GetObject(ObjectType::Uint8ClampedArray);
  514. #endif
  515. break;
  516. case Js::JavascriptBuiltInFunction::Int16Array_NewInstance:
  517. #ifdef _M_X64
  518. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Int16MixedArray) : ValueType::GetObject(ObjectType::Int16Array);
  519. #else
  520. *returnType = ValueType::GetObject(ObjectType::Int16Array);
  521. #endif
  522. break;
  523. case Js::JavascriptBuiltInFunction::Uint16Array_NewInstance:
  524. #ifdef _M_X64
  525. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Uint16MixedArray) : ValueType::GetObject(ObjectType::Uint16Array);
  526. #else
  527. *returnType = ValueType::GetObject(ObjectType::Uint16Array);
  528. #endif
  529. break;
  530. case Js::JavascriptBuiltInFunction::Int32Array_NewInstance:
  531. #ifdef _M_X64
  532. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Int32MixedArray) : ValueType::GetObject(ObjectType::Int32Array);
  533. #else
  534. *returnType = ValueType::GetObject(ObjectType::Int32Array);
  535. #endif
  536. break;
  537. case Js::JavascriptBuiltInFunction::Uint32Array_NewInstance:
  538. #ifdef _M_X64
  539. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Uint32MixedArray) : ValueType::GetObject(ObjectType::Uint32Array);
  540. #else
  541. *returnType = ValueType::GetObject(ObjectType::Uint32Array);
  542. #endif
  543. break;
  544. case Js::JavascriptBuiltInFunction::Float32Array_NewInstance:
  545. #ifdef _M_X64
  546. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Float32MixedArray) : ValueType::GetObject(ObjectType::Float32Array);
  547. #else
  548. *returnType = ValueType::GetObject(ObjectType::Float32Array);
  549. #endif
  550. break;
  551. case Js::JavascriptBuiltInFunction::Float64Array_NewInstance:
  552. #ifdef _M_X64
  553. *returnType = (!PHASE_OFF1(Js::TypedArrayVirtualPhase)) ? ValueType::GetObject(ObjectType::Float64MixedArray) : ValueType::GetObject(ObjectType::Float64Array);
  554. #else
  555. *returnType = ValueType::GetObject(ObjectType::Float64Array);
  556. #endif
  557. break;
  558. case Js::JavascriptBuiltInFunction::Int64Array_NewInstance:
  559. *returnType = ValueType::GetObject(ObjectType::Int64Array);
  560. break;
  561. case Js::JavascriptBuiltInFunction::Uint64Array_NewInstance:
  562. *returnType = ValueType::GetObject(ObjectType::Uint64Array);
  563. break;
  564. case Js::JavascriptBuiltInFunction::BoolArray_NewInstance:
  565. *returnType = ValueType::GetObject(ObjectType::BoolArray);
  566. break;
  567. case Js::JavascriptBuiltInFunction::CharArray_NewInstance:
  568. *returnType = ValueType::GetObject(ObjectType::CharArray);
  569. break;
  570. default:
  571. return false;
  572. }
  573. return true;
  574. }
  575. bool InliningDecider::CanRecursivelyInline(Js::FunctionBody * inlinee, Js::FunctionBody *inliner, bool allowRecursiveInlining, uint recursiveInlineDepth)
  576. {
  577. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  578. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  579. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  580. #endif
  581. if (!PHASE_OFF(Js::InlineRecursivePhase, inliner)
  582. && allowRecursiveInlining
  583. && inlinee == inliner
  584. && inlinee->CanInlineRecursively(recursiveInlineDepth))
  585. {
  586. INLINE_TESTTRACE(_u("INLINING: Inlined recursively\tInlinee: %s (%s)\tCaller: %s (%s)\tDepth: %d\n"),
  587. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  588. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), recursiveInlineDepth);
  589. return true;
  590. }
  591. if (!inlinee->CanInlineAgain())
  592. {
  593. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Do not inline recursive functions\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
  594. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  595. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2));
  596. return false;
  597. }
  598. return true;
  599. }
  600. // This only enables collection of the inlinee data, we are much more aggressive here.
  601. // Actual decision of whether something is inlined or not is taken in CommitInlineIntoInliner
  602. bool InliningDecider::DeciderInlineIntoInliner(Js::FunctionBody * inlinee, Js::FunctionBody * inliner, bool isConstructorCall, bool isPolymorphicCall, uint16 constantArgInfo, uint recursiveInlineDepth, bool allowRecursiveInlining)
  603. {
  604. if (!CanRecursivelyInline(inlinee, inliner, allowRecursiveInlining, recursiveInlineDepth))
  605. {
  606. return false;
  607. }
  608. if (inliner->GetIsAsmjsMode() != inlinee->GetIsAsmjsMode())
  609. {
  610. return false;
  611. }
  612. // Force inline all Js Builtins functions
  613. // The existing JsBuiltInForceInline flag can work only when we explictly create scriptFunction
  614. // We can also have methods that we define on the prototype like next of ArrayIterator for which we don't explictly create a script function
  615. // TODO (megupta) : use forceInline for methods defined on the prototype using ObjectDefineProperty
  616. if (inlinee->GetSourceContextId() == Js::Constants::JsBuiltInSourceContextId ||
  617. PHASE_FORCE(Js::InlinePhase, this->topFunc) ||
  618. PHASE_FORCE(Js::InlinePhase, inliner) ||
  619. PHASE_FORCE(Js::InlinePhase, inlinee))
  620. {
  621. return true;
  622. }
  623. if (PHASE_OFF(Js::InlinePhase, this->topFunc) ||
  624. PHASE_OFF(Js::InlinePhase, inliner) ||
  625. PHASE_OFF(Js::InlinePhase, inlinee))
  626. {
  627. return false;
  628. }
  629. if (PHASE_FORCE(Js::InlineTreePhase, this->topFunc) ||
  630. PHASE_FORCE(Js::InlineTreePhase, inliner))
  631. {
  632. return true;
  633. }
  634. if (PHASE_FORCE(Js::InlineAtEveryCallerPhase, inlinee))
  635. {
  636. return true;
  637. }
  638. uint inlineeByteCodeCount = inlinee->GetByteCodeWithoutLDACount();
  639. // Heuristics are hit in the following order (Note *order* is important)
  640. // 1. Leaf function: If the inlinee is a leaf (but not a constructor or a polymorphic call) inline threshold is LeafInlineThreshold (60). Also it can have max 1 loop
  641. // 2. Constant Function Argument: If the inlinee candidate has a constant argument and that argument is used for branching, then the inline threshold is ConstantArgumentInlineThreshold (157)
  642. // 3. InlineThreshold: If an inlinee candidate exceeds InlineThreshold just don't inline no matter what.
  643. // Following are additional constraint for an inlinee which meets InlineThreshold (Rule no 3)
  644. // 4. Rule for inlinee with loops:
  645. // 4a. Only single loop in inlinee is permitted.
  646. // 4b. Should not have polymorphic field access.
  647. // 4c. Should not be a constructor.
  648. // 4d. Should meet LoopInlineThreshold (25)
  649. // 5. Rule for polymorphic inlinee:
  650. // 4a. Should meet PolymorphicInlineThreshold (32)
  651. // 6. Rule for constructors:
  652. // 5a. Always inline if inlinee has polymorphic field access (as we have cloned runtime data).
  653. // 5b. If inlinee is monomorphic, inline only small constructors. They are governed by ConstructorInlineThreshold (21)
  654. // 7. Rule for inlinee which is not interpreted enough (as we might not have all the profile data):
  655. // 7a. As of now it is still governed by the InlineThreshold. Plan to play with this in future.
  656. // 8. Rest should be inlined.
  657. uint16 mask = constantArgInfo & inlinee->m_argUsedForBranch;
  658. if (mask && inlineeByteCodeCount < (uint)CONFIG_FLAG(ConstantArgumentInlineThreshold))
  659. {
  660. return true;
  661. }
  662. int inlineThreshold = threshold.inlineThreshold;
  663. if (!isPolymorphicCall && !isConstructorCall && IsInlineeLeaf(inlinee) && (inlinee->GetLoopCount() <= 2))
  664. {
  665. // Inlinee is a leaf function
  666. if (inlinee->GetLoopCount() == 0 || GetNumberOfInlineesWithLoop() <= (uint)threshold.maxNumberOfInlineesWithLoop) // Don't inlinee too many inlinees with loops.
  667. {
  668. // Negative LeafInlineThreshold disable the threshold
  669. if (threshold.leafInlineThreshold >= 0)
  670. {
  671. inlineThreshold += threshold.leafInlineThreshold - threshold.inlineThreshold;
  672. }
  673. }
  674. }
  675. #if ENABLE_DEBUG_CONFIG_OPTIONS
  676. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  677. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  678. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  679. #endif
  680. if (inlinee->GetHasLoops())
  681. {
  682. if (threshold.loopInlineThreshold < 0 || // Negative LoopInlineThreshold disable inlining with loop
  683. GetNumberOfInlineesWithLoop() >(uint)threshold.maxNumberOfInlineesWithLoop || // See if we are inlining too many inlinees with loops.
  684. (inlinee->GetLoopCount() > 2) || // Allow at most 2 loops.
  685. inlinee->GetHasNestedLoop() || // Nested loops are not a good inlinee candidate
  686. isConstructorCall || // If the function is constructor with loops, don't inline.
  687. PHASE_OFF(Js::InlineFunctionsWithLoopsPhase, this->topFunc))
  688. {
  689. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has loops \tBytecode size: %d \tgetNumberOfInlineesWithLoop: %d\tloopCount: %d\thasNestedLoop: %d\tisConstructorCall:%d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
  690. inlinee->GetByteCodeCount(),
  691. GetNumberOfInlineesWithLoop(),
  692. inlinee->GetLoopCount(),
  693. inlinee->GetHasNestedLoop(),
  694. isConstructorCall,
  695. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  696. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  697. topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
  698. // Don't inline function with loops
  699. return false;
  700. }
  701. else
  702. {
  703. inlineThreshold -= (threshold.inlineThreshold > threshold.loopInlineThreshold) ? threshold.inlineThreshold - threshold.loopInlineThreshold : 0;
  704. }
  705. }
  706. if (isPolymorphicCall)
  707. {
  708. if (threshold.polymorphicInlineThreshold < 0 || // Negative PolymorphicInlineThreshold disable inlining
  709. isConstructorCall)
  710. {
  711. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Polymorphic call under PolymorphicInlineThreshold: %d \tBytecode size: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
  712. threshold.polymorphicInlineThreshold,
  713. inlinee->GetByteCodeCount(),
  714. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  715. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  716. topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
  717. return false;
  718. }
  719. else
  720. {
  721. inlineThreshold -= (threshold.inlineThreshold > threshold.polymorphicInlineThreshold) ? threshold.inlineThreshold - threshold.polymorphicInlineThreshold : 0;
  722. }
  723. }
  724. if (isConstructorCall)
  725. {
  726. #pragma prefast(suppress: 6285, "logical-or of constants is by design")
  727. if (PHASE_OFF(Js::InlineConstructorsPhase, this->topFunc) ||
  728. PHASE_OFF(Js::InlineConstructorsPhase, inliner) ||
  729. PHASE_OFF(Js::InlineConstructorsPhase, inlinee) ||
  730. !CONFIG_FLAG(CloneInlinedPolymorphicCaches))
  731. {
  732. return false;
  733. }
  734. if (PHASE_FORCE(Js::InlineConstructorsPhase, this->topFunc) ||
  735. PHASE_FORCE(Js::InlineConstructorsPhase, inliner) ||
  736. PHASE_FORCE(Js::InlineConstructorsPhase, inlinee))
  737. {
  738. return true;
  739. }
  740. if (inlinee->HasDynamicProfileInfo() && inlinee->GetAnyDynamicProfileInfo()->HasPolymorphicFldAccess())
  741. {
  742. // As of now this is not dependent on bytecodeInlinedThreshold.
  743. return true;
  744. }
  745. // Negative ConstructorInlineThreshold always disable constructor inlining
  746. if (threshold.constructorInlineThreshold < 0)
  747. {
  748. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Constructor with no polymorphic field access \tBytecode size: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
  749. inlinee->GetByteCodeCount(),
  750. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  751. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  752. topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
  753. // Don't inline constructor that does not have a polymorphic field access, or if cloning polymorphic inline
  754. // caches is disabled
  755. return false;
  756. }
  757. else
  758. {
  759. inlineThreshold -= (threshold.inlineThreshold > threshold.constructorInlineThreshold) ? threshold.inlineThreshold - threshold.constructorInlineThreshold : 0;
  760. }
  761. }
  762. if (threshold.forLoopBody)
  763. {
  764. inlineThreshold /= CONFIG_FLAG(InlineInLoopBodyScaleDownFactor);
  765. }
  766. if (inlineThreshold > 0 && inlineeByteCodeCount <= (uint)inlineThreshold)
  767. {
  768. if (inlinee->GetLoopCount())
  769. {
  770. IncrementNumberOfInlineesWithLoop();
  771. }
  772. return true;
  773. }
  774. else
  775. {
  776. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Too long \tBytecode size: %d\tThreshold: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
  777. inlinee->GetByteCodeCount(),
  778. inlineThreshold,
  779. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
  780. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
  781. topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
  782. return false;
  783. }
  784. }
  785. bool InliningDecider::ContinueInliningUserDefinedFunctions(uint32 bytecodeInlinedCount) const
  786. {
  787. #if ENABLE_DEBUG_CONFIG_OPTIONS
  788. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  789. #endif
  790. if (PHASE_FORCE(Js::InlinePhase, this->topFunc) || bytecodeInlinedCount <= (uint)this->threshold.inlineCountMax)
  791. {
  792. return true;
  793. }
  794. INLINE_TESTTRACE(_u("INLINING: Skip Inline: InlineCountMax threshold %d, reached: %s (#%s)\n"),
  795. (uint)this->threshold.inlineCountMax,
  796. this->topFunc->GetDisplayName(), this->topFunc->GetDebugNumberSet(debugStringBuffer));
  797. return false;
  798. }
  799. #if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  800. // static
  801. void InliningDecider::TraceInlining(Js::FunctionBody *const inliner, const char16* inlineeName, const char16* inlineeFunctionIdandNumberString, uint inlineeByteCodeCount,
  802. Js::FunctionBody* topFunc, uint inlinedByteCodeCount, Js::FunctionBody *const inlinee, uint callSiteId, bool inLoopBody, bool isCallback, uint builtIn)
  803. {
  804. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  805. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  806. char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  807. if (inlineeName == nullptr)
  808. {
  809. int len = swprintf_s(debugStringBuffer3, MAX_FUNCTION_BODY_DEBUG_STRING_SIZE, _u("built In Id: %u"), builtIn);
  810. Assert(len > 14);
  811. inlineeName = debugStringBuffer3;
  812. }
  813. INLINE_TRACE_AND_TESTTRACE(_u("INLINING%s %s: Inlinee: %s (%s)\tSize: %d\tCaller: %s (%s)\tSize: %d\tInlineCount: %d\tRoot: %s (%s)\tSize: %d\tCallSiteId: %d\n"),
  814. isCallback ? _u(" CALLBACK") : _u(""),
  815. inLoopBody ? _u(" IN LOOP BODY") : _u(""),
  816. inlineeName, inlineeFunctionIdandNumberString, inlineeByteCodeCount,
  817. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer), inliner->GetByteCodeCount(),
  818. inlinedByteCodeCount,
  819. topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer2), topFunc->GetByteCodeCount(),
  820. callSiteId
  821. );
  822. // Now Trace inlining across files cases
  823. if (builtIn != -1) // built-in functions
  824. {
  825. return;
  826. }
  827. Assert(inliner && inlinee);
  828. if (inliner->GetSourceContextId() != inlinee->GetSourceContextId())
  829. {
  830. INLINE_TRACE_AND_TESTTRACE(_u("INLINING_ACROSS_FILES: Inlinee: %s (%s)\tSize: %d\tCaller: %s (%s)\tSize: %d\tInlineCount: %d\tRoot: %s (%s)\tSize: %d\n"),
  831. inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetByteCodeCount(),
  832. inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), inliner->GetByteCodeCount(), inlinedByteCodeCount,
  833. topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3), topFunc->GetByteCodeCount()
  834. );
  835. }
  836. }
  837. #endif