InliningDecider.cpp 39 KB

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