InliningDecider.cpp 41 KB

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