BailOut.cpp 134 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. #include "Debug/DebuggingFlags.h"
  7. #include "Debug/DiagProbe.h"
  8. #include "Debug/DebugManager.h"
  9. #include "Language/JavascriptFunctionArgIndex.h"
  10. extern const IRType RegTypes[RegNumCount];
  11. void
  12. BailOutInfo::Clear(JitArenaAllocator * allocator)
  13. {
  14. // Currently, we don't have a case where we delete bailout info after we allocated the bailout record
  15. Assert(!bailOutRecord);
  16. this->capturedValues.constantValues.Clear(allocator);
  17. this->capturedValues.copyPropSyms.Clear(allocator);
  18. this->usedCapturedValues.constantValues.Clear(allocator);
  19. this->usedCapturedValues.copyPropSyms.Clear(allocator);
  20. if (byteCodeUpwardExposedUsed)
  21. {
  22. JitAdelete(allocator, byteCodeUpwardExposedUsed);
  23. }
  24. if (startCallInfo)
  25. {
  26. Assert(argOutSyms);
  27. JitAdeleteArray(allocator, startCallCount, startCallInfo);
  28. JitAdeleteArray(allocator, totalOutParamCount, argOutSyms);
  29. }
  30. if (liveVarSyms)
  31. {
  32. JitAdelete(allocator, liveVarSyms);
  33. JitAdelete(allocator, liveLosslessInt32Syms);
  34. JitAdelete(allocator, liveFloat64Syms);
  35. }
  36. #ifdef _M_IX86
  37. if (outParamFrameAdjustArgSlot)
  38. {
  39. JitAdelete(allocator, outParamFrameAdjustArgSlot);
  40. }
  41. #endif
  42. }
  43. #ifdef _M_IX86
  44. uint
  45. BailOutInfo::GetStartCallOutParamCount(uint i) const
  46. {
  47. Assert(i < this->startCallCount);
  48. Assert(this->startCallInfo);
  49. return this->startCallInfo[i].argCount;
  50. }
  51. bool
  52. BailOutInfo::NeedsStartCallAdjust(uint i, const IR::Instr * bailOutInstr) const
  53. {
  54. Assert(i < this->startCallCount);
  55. Assert(this->startCallInfo);
  56. Assert(bailOutInstr->m_func->HasInstrNumber());
  57. IR::Instr * instr = this->startCallInfo[i].instr;
  58. if (instr == nullptr || instr->m_opcode == Js::OpCode::StartCall)
  59. {
  60. // The StartCall was unlinked (because it was being deleted, or we know it was
  61. // moved below the bailout instr).
  62. // -------- or --------
  63. // StartCall wasn't lowered because the argouts were orphaned, in which case we don't need
  64. // the adjustment as the orphaned argouts are not stored with the non-orphaned ones
  65. return false;
  66. }
  67. // In scenarios related to partial polymorphic inlining where we move the lowered version of the start call - LEA esp, esp - argcount * 4
  68. // next to the call itself as part of one of the dispatch arms. In this scenario StartCall is marked
  69. // as cloned and we do not need to adjust the offsets from where the args need to be restored.
  70. return instr->GetNumber() < bailOutInstr->GetNumber() && !instr->IsCloned();
  71. }
  72. void
  73. BailOutInfo::RecordStartCallInfo(uint i, uint argRestoreAdjustCount, IR::Instr *instr)
  74. {
  75. Assert(i < this->startCallCount);
  76. Assert(this->startCallInfo);
  77. Assert(instr);
  78. Assert(instr->m_opcode == Js::OpCode::StartCall);
  79. this->startCallInfo[i].instr = instr;
  80. this->startCallInfo[i].argCount = instr->GetArgOutCount(/*getInterpreterArgOutCount*/ true);
  81. this->startCallInfo[i].argRestoreAdjustCount = argRestoreAdjustCount;
  82. }
  83. void
  84. BailOutInfo::UnlinkStartCall(const IR::Instr * instr)
  85. {
  86. Assert(this->startCallCount == 0 || this->startCallInfo != nullptr);
  87. uint i;
  88. for (i = 0; i < this->startCallCount; i++)
  89. {
  90. StartCallInfo *info = &this->startCallInfo[i];
  91. if (info->instr == instr)
  92. {
  93. info->instr = nullptr;
  94. return;
  95. }
  96. }
  97. }
  98. #else
  99. uint
  100. BailOutInfo::GetStartCallOutParamCount(uint i) const
  101. {
  102. Assert(i < this->startCallCount);
  103. Assert(this->startCallInfo);
  104. return this->startCallInfo[i];
  105. }
  106. void
  107. BailOutInfo::RecordStartCallInfo(uint i, uint argRestoreAdjust, IR::Instr *instr)
  108. {
  109. Assert(i < this->startCallCount);
  110. Assert(this->startCallInfo);
  111. Assert(instr);
  112. Assert(instr->m_opcode == Js::OpCode::StartCall);
  113. Assert(instr->GetSrc1());
  114. this->startCallInfo[i] = instr->GetArgOutCount(/*getInterpreterArgOutCount*/ true);
  115. }
  116. #endif
  117. #ifdef MD_GROW_LOCALS_AREA_UP
  118. void
  119. BailOutInfo::FinalizeOffsets(__in_ecount(count) int * offsets, uint count, Func *func, BVSparse<JitArenaAllocator> *bvInlinedArgSlot)
  120. {
  121. // Turn positive SP-relative sym offsets into negative frame-pointer-relative offsets for the convenience
  122. // of the restore-value logic.
  123. int32 inlineeArgStackSize = func->GetInlineeArgumentStackSize();
  124. int localsSize = func->m_localStackHeight + func->m_ArgumentsOffset;
  125. for (uint i = 0; i < count; i++)
  126. {
  127. int offset = -(offsets[i] + StackSymBias);
  128. if (offset < 0)
  129. {
  130. // Not stack offset
  131. continue;
  132. }
  133. if (bvInlinedArgSlot && bvInlinedArgSlot->Test(i))
  134. {
  135. // Inlined out param: the positive offset is relative to the start of the inlinee arg area,
  136. // so we need to subtract the full locals area (including the inlined-arg-area) to get the proper result.
  137. offset -= localsSize;
  138. }
  139. else
  140. {
  141. // The locals size contains the inlined-arg-area size, so remove the inlined-arg-area size from the
  142. // adjustment for normal locals whose offsets are relative to the start of the locals area.
  143. offset -= (localsSize - inlineeArgStackSize);
  144. }
  145. Assert(offset < 0);
  146. offsets[i] = offset;
  147. }
  148. }
  149. #endif
  150. void
  151. BailOutInfo::FinalizeBailOutRecord(Func * func)
  152. {
  153. Assert(func->IsTopFunc());
  154. BailOutRecord * bailOutRecord = this->bailOutRecord;
  155. if (bailOutRecord == nullptr)
  156. {
  157. return;
  158. }
  159. BailOutRecord * currentBailOutRecord = bailOutRecord;
  160. Func * currentBailOutFunc = this->bailOutFunc;
  161. // Top of the inlined arg stack is at the beginning of the locals, find the offset from EBP+2
  162. #ifdef MD_GROW_LOCALS_AREA_UP
  163. uint inlinedArgSlotAdjust = (func->m_localStackHeight + func->m_ArgumentsOffset);
  164. #else
  165. uint inlinedArgSlotAdjust = (func->m_localStackHeight + (2 * MachPtr));
  166. #endif
  167. while (currentBailOutRecord->parent != nullptr)
  168. {
  169. Assert(currentBailOutRecord->globalBailOutRecordTable->firstActualStackOffset == -1 ||
  170. currentBailOutRecord->globalBailOutRecordTable->firstActualStackOffset == (int32)(currentBailOutFunc->firstActualStackOffset - inlinedArgSlotAdjust));
  171. Assert(!currentBailOutFunc->IsTopFunc());
  172. Assert(currentBailOutFunc->firstActualStackOffset != -1);
  173. // Find the top of the locals on the stack from EBP
  174. currentBailOutRecord->globalBailOutRecordTable->firstActualStackOffset = currentBailOutFunc->firstActualStackOffset - inlinedArgSlotAdjust;
  175. currentBailOutRecord = currentBailOutRecord->parent;
  176. currentBailOutFunc = currentBailOutFunc->GetParentFunc();
  177. }
  178. Assert(currentBailOutRecord->globalBailOutRecordTable->firstActualStackOffset == -1);
  179. Assert(currentBailOutFunc->IsTopFunc());
  180. Assert(currentBailOutFunc->firstActualStackOffset == -1);
  181. #ifndef MD_GROW_LOCALS_AREA_UP
  182. if (this->totalOutParamCount != 0)
  183. {
  184. if (func->HasInlinee())
  185. {
  186. FOREACH_BITSET_IN_SPARSEBV(index, this->outParamInlinedArgSlot)
  187. {
  188. this->outParamOffsets[index] -= inlinedArgSlotAdjust;
  189. }
  190. NEXT_BITSET_IN_SPARSEBV;
  191. }
  192. #ifdef _M_IX86
  193. int frameSize = func->frameSize;
  194. AssertMsg(frameSize != 0, "Frame size not calculated");
  195. FOREACH_BITSET_IN_SPARSEBV(index, this->outParamFrameAdjustArgSlot)
  196. {
  197. this->outParamOffsets[index] -= frameSize;
  198. }
  199. NEXT_BITSET_IN_SPARSEBV;
  200. #endif
  201. }
  202. #else
  203. if (func->IsJitInDebugMode())
  204. {
  205. // Turn positive SP-relative base locals offset into negative frame-pointer-relative offset
  206. func->AjustLocalVarSlotOffset();
  207. }
  208. currentBailOutRecord = bailOutRecord;
  209. int32 inlineeArgStackSize = func->GetInlineeArgumentStackSize();
  210. do
  211. {
  212. // Note: do this only once
  213. currentBailOutRecord->globalBailOutRecordTable->VisitGlobalBailOutRecordTableRowsAtFirstBailOut(
  214. currentBailOutRecord->m_bailOutRecordId, [=](GlobalBailOutRecordDataRow *row) {
  215. int offset = -(row->offset + StackSymBias);
  216. if (offset < 0)
  217. {
  218. // Not stack offset
  219. return;
  220. }
  221. // The locals size contains the inlined-arg-area size, so remove the inlined-arg-area size from the
  222. // adjustment for normal locals whose offsets are relative to the start of the locals area.
  223. offset -= (inlinedArgSlotAdjust - inlineeArgStackSize);
  224. Assert(offset < 0);
  225. row->offset = offset;
  226. });
  227. // Only adjust once
  228. int forInEnumeratorArrayRestoreOffset = currentBailOutRecord->globalBailOutRecordTable->forInEnumeratorArrayRestoreOffset;
  229. if (forInEnumeratorArrayRestoreOffset >= 0)
  230. {
  231. forInEnumeratorArrayRestoreOffset -= (inlinedArgSlotAdjust - inlineeArgStackSize);
  232. Assert(forInEnumeratorArrayRestoreOffset < 0);
  233. currentBailOutRecord->globalBailOutRecordTable->forInEnumeratorArrayRestoreOffset = forInEnumeratorArrayRestoreOffset;
  234. }
  235. currentBailOutRecord = currentBailOutRecord->parent;
  236. }
  237. while (currentBailOutRecord != nullptr);
  238. this->FinalizeOffsets(this->outParamOffsets, this->totalOutParamCount, func, func->HasInlinee() ? this->outParamInlinedArgSlot : nullptr);
  239. #endif
  240. // set the bailOutRecord to null so we don't adjust it again if the info is shared
  241. bailOutRecord = nullptr;
  242. }
  243. #if DBG
  244. bool
  245. BailOutInfo::IsBailOutHelper(IR::JnHelperMethod helper)
  246. {
  247. switch (helper)
  248. {
  249. case IR::HelperSaveAllRegistersAndBailOut:
  250. case IR::HelperSaveAllRegistersAndBranchBailOut:
  251. #ifdef _M_IX86
  252. case IR::HelperSaveAllRegistersNoSse2AndBailOut:
  253. case IR::HelperSaveAllRegistersNoSse2AndBranchBailOut:
  254. #endif
  255. return true;
  256. };
  257. return false;
  258. };
  259. #endif
  260. //===================================================================================================================================
  261. // BailOutRecord
  262. //===================================================================================================================================
  263. BailOutRecord::BailOutRecord(uint32 bailOutOffset, uint bailOutCacheIndex, IR::BailOutKind kind, Func * bailOutFunc) :
  264. argOutOffsetInfo(nullptr), bailOutOffset(bailOutOffset),
  265. bailOutCount(0), polymorphicCacheIndex(bailOutCacheIndex), bailOutKind(kind),
  266. branchValueRegSlot(Js::Constants::NoRegister),
  267. ehBailoutData(nullptr), m_bailOutRecordId(0), type(Normal)
  268. #if DBG
  269. , inlineDepth(0)
  270. #endif
  271. {
  272. CompileAssert(offsetof(BailOutRecord, globalBailOutRecordTable) == 0); // the offset is hard-coded in LinearScanMD::SaveAllRegisters
  273. CompileAssert(offsetof(GlobalBailOutRecordDataTable, registerSaveSpace) == 0); // the offset is hard-coded in LinearScanMD::SaveAllRegisters}
  274. Assert(bailOutOffset != Js::Constants::NoByteCodeOffset);
  275. #if DBG
  276. actualCount = bailOutFunc->actualCount;
  277. Assert(bailOutFunc->IsTopFunc() || actualCount != -1);
  278. #endif
  279. }
  280. #if ENABLE_DEBUG_CONFIG_OPTIONS
  281. #define REJIT_KIND_TESTTRACE(bailOutKind, ...) \
  282. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::ReJITPhase)) \
  283. { \
  284. if (Js::Configuration::Global.flags.RejitTraceFilter.Empty() || Js::Configuration::Global.flags.RejitTraceFilter.Contains(bailOutKind)) \
  285. { \
  286. Output::Print(__VA_ARGS__); \
  287. Output::Flush(); \
  288. } \
  289. }
  290. const char16 * const trueString = _u("true");
  291. const char16 * const falseString = _u("false");
  292. #else
  293. #define REJIT_KIND_TESTTRACE(...)
  294. #endif
  295. #if ENABLE_DEBUG_CONFIG_OPTIONS
  296. #define BAILOUT_KIND_TRACE(functionBody, bailOutKind, ...) \
  297. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::BailOutPhase, functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId()) && \
  298. ((bailOutKind) != IR::BailOnSimpleJitToFullJitLoopBody || CONFIG_FLAG(Verbose))) \
  299. { \
  300. if (Js::Configuration::Global.flags.BailoutTraceFilter.Empty() || Js::Configuration::Global.flags.BailoutTraceFilter.Contains(bailOutKind)) \
  301. { \
  302. Output::Print(__VA_ARGS__); \
  303. if (bailOutKind != IR::BailOutInvalid) \
  304. { \
  305. Output::Print(_u(" Kind: %S"), ::GetBailOutKindName(bailOutKind)); \
  306. } \
  307. Output::Print(_u("\n")); \
  308. } \
  309. }
  310. #define BAILOUT_VERBOSE_TRACE(functionBody, bailOutKind, ...) \
  311. if (Js::Configuration::Global.flags.Verbose && Js::Configuration::Global.flags.Trace.IsEnabled(Js::BailOutPhase,functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId())) \
  312. { \
  313. if (Js::Configuration::Global.flags.BailoutTraceFilter.Empty() || Js::Configuration::Global.flags.BailoutTraceFilter.Contains(bailOutKind)) \
  314. { \
  315. Output::Print(__VA_ARGS__); \
  316. } \
  317. }
  318. #define BAILOUT_TESTTRACE(functionBody, bailOutKind, ...) \
  319. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::BailOutPhase, functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId()) && \
  320. ((bailOutKind) != IR::BailOnSimpleJitToFullJitLoopBody || CONFIG_FLAG(Verbose))) \
  321. { \
  322. if (Js::Configuration::Global.flags.BailoutTraceFilter.Empty() || Js::Configuration::Global.flags.BailoutTraceFilter.Contains(bailOutKind)) \
  323. { \
  324. Output::Print(__VA_ARGS__); \
  325. } \
  326. }
  327. #define BAILOUT_FLUSH(functionBody) \
  328. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::BailOutPhase, functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId()) || \
  329. Js::Configuration::Global.flags.Trace.IsEnabled(Js::BailOutPhase, functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId())) \
  330. { \
  331. Output::Flush(); \
  332. }
  333. #else
  334. #define BAILOUT_KIND_TRACE(functionBody, bailOutKind, ...)
  335. #define BAILOUT_TESTTRACE(functionBody, bailOutKind, ...)
  336. #define BAILOUT_VERBOSE_TRACE(functionBody, bailOutKind, ...)
  337. #define BAILOUT_FLUSH(functionBody)
  338. #endif
  339. #if DBG
  340. void BailOutRecord::DumpArgOffsets(uint count, int* offsets, int argOutSlotStart)
  341. {
  342. char16 const * name = _u("OutParam");
  343. Js::RegSlot regSlotOffset = 0;
  344. for (uint i = 0; i < count; i++)
  345. {
  346. int offset = offsets[i];
  347. // The variables below determine whether we have a Var or native float/int.
  348. bool isFloat64 = this->argOutOffsetInfo->argOutFloat64Syms->Test(argOutSlotStart + i) != 0;
  349. bool isInt32 = this->argOutOffsetInfo->argOutLosslessInt32Syms->Test(argOutSlotStart + i) != 0;
  350. #ifdef ENABLE_SIMDJS
  351. // SIMD_JS
  352. // Simd128 reside in Float64 regs
  353. isFloat64 |= this->argOutOffsetInfo->argOutSimd128F4Syms->Test(argOutSlotStart + i) != 0;
  354. isFloat64 |= this->argOutOffsetInfo->argOutSimd128I4Syms->Test(argOutSlotStart + i) != 0;
  355. isFloat64 |= this->argOutOffsetInfo->argOutSimd128I8Syms->Test(argOutSlotStart + i) != 0;
  356. isFloat64 |= this->argOutOffsetInfo->argOutSimd128I16Syms->Test(argOutSlotStart + i) != 0;
  357. isFloat64 |= this->argOutOffsetInfo->argOutSimd128U4Syms->Test(argOutSlotStart + i) != 0;
  358. isFloat64 |= this->argOutOffsetInfo->argOutSimd128U8Syms->Test(argOutSlotStart + i) != 0;
  359. isFloat64 |= this->argOutOffsetInfo->argOutSimd128U16Syms->Test(argOutSlotStart + i) != 0;
  360. isFloat64 |= this->argOutOffsetInfo->argOutSimd128B4Syms->Test(argOutSlotStart + i) != 0;
  361. isFloat64 |= this->argOutOffsetInfo->argOutSimd128B8Syms->Test(argOutSlotStart + i) != 0;
  362. isFloat64 |= this->argOutOffsetInfo->argOutSimd128B16Syms->Test(argOutSlotStart + i) != 0;
  363. #endif
  364. Assert(!isFloat64 || !isInt32);
  365. Output::Print(_u("%s #%3d: "), name, i + regSlotOffset);
  366. this->DumpValue(offset, isFloat64);
  367. Output::Print(_u("\n"));
  368. }
  369. }
  370. void BailOutRecord::DumpLocalOffsets(uint count, int argOutSlotStart)
  371. {
  372. char16 const * name = _u("Register");
  373. globalBailOutRecordTable->IterateGlobalBailOutRecordTableRows(m_bailOutRecordId, [=](GlobalBailOutRecordDataRow *row) {
  374. Assert(row != nullptr);
  375. // The variables below determine whether we have a Var or native float/int.
  376. bool isFloat64 = row->isFloat;
  377. bool isInt32 = row->isInt;
  378. #ifdef ENABLE_SIMDJS
  379. // SIMD_JS
  380. // Simd values are in float64 regs
  381. isFloat64 = isFloat64 || row->isSimd128F4;
  382. isFloat64 = isFloat64 || row->isSimd128I4;
  383. isFloat64 = isFloat64 || row->isSimd128I8;
  384. isFloat64 = isFloat64 || row->isSimd128I16;
  385. isFloat64 = isFloat64 || row->isSimd128U4;
  386. isFloat64 = isFloat64 || row->isSimd128U8;
  387. isFloat64 = isFloat64 || row->isSimd128U16;
  388. isFloat64 = isFloat64 || row->isSimd128B4;
  389. isFloat64 = isFloat64 || row->isSimd128B8;
  390. isFloat64 = isFloat64 || row->isSimd128B16;
  391. #endif
  392. Assert(!isFloat64 || !isInt32);
  393. Output::Print(_u("%s #%3d: "), name, row->regSlot);
  394. this->DumpValue(row->offset, isFloat64);
  395. Output::Print(_u("\n"));
  396. });
  397. }
  398. void BailOutRecord::DumpValue(int offset, bool isFloat64)
  399. {
  400. if (offset < 0)
  401. {
  402. Output::Print(_u("Stack offset %6d"), offset);
  403. }
  404. else if (offset > 0)
  405. {
  406. if ((uint)offset <= GetBailOutRegisterSaveSlotCount())
  407. {
  408. if (isFloat64)
  409. {
  410. #ifdef _M_ARM
  411. Output::Print(_u("Register %-4S %4d"), RegNames[(offset - RegD0) / 2 + RegD0], offset);
  412. #else
  413. Output::Print(_u("Register %-4S %4d"), RegNames[offset], offset);
  414. #endif
  415. }
  416. else
  417. {
  418. Output::Print(_u("Register %-4S %4d"), RegNames[offset], offset);
  419. }
  420. }
  421. else if (BailOutRecord::IsArgumentsObject((uint)offset))
  422. {
  423. Output::Print(_u("Arguments object"));
  424. }
  425. else
  426. {
  427. // Constants offset starts from max bail out register save slot count
  428. uint constantIndex = offset - (GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount()) - 1;
  429. Output::Print(_u("Constant index %4d value:0x%p (Var)"), constantIndex, this->constants[constantIndex]);
  430. Assert(!isFloat64);
  431. }
  432. }
  433. else
  434. {
  435. Output::Print(_u("Not live"));
  436. }
  437. }
  438. void BailOutRecord::Dump()
  439. {
  440. if (this->localOffsetsCount)
  441. {
  442. Output::Print(_u("**** Locals ***\n"));
  443. DumpLocalOffsets(this->localOffsetsCount, 0);
  444. }
  445. uint outParamSlot = 0;
  446. if(this->argOutOffsetInfo)
  447. {
  448. Output::Print(_u("**** Out params ***\n"));
  449. for (uint i = 0; i < this->argOutOffsetInfo->startCallCount; i++)
  450. {
  451. uint startCallOutParamCount = this->argOutOffsetInfo->startCallOutParamCounts[i];
  452. DumpArgOffsets(startCallOutParamCount, &this->argOutOffsetInfo->outParamOffsets[outParamSlot], this->argOutOffsetInfo->argOutSymStart + outParamSlot);
  453. outParamSlot += startCallOutParamCount;
  454. }
  455. }
  456. }
  457. #endif
  458. /*static*/
  459. bool BailOutRecord::IsArgumentsObject(uint32 offset)
  460. {
  461. bool isArgumentsObject = (GetArgumentsObjectOffset() == offset);
  462. return isArgumentsObject;
  463. }
  464. /*static*/
  465. uint32 BailOutRecord::GetArgumentsObjectOffset()
  466. {
  467. uint32 argumentsObjectOffset = (GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount());
  468. return argumentsObjectOffset;
  469. }
  470. Js::Var BailOutRecord::EnsureArguments(Js::InterpreterStackFrame * newInstance, Js::JavascriptCallStackLayout * layout, Js::ScriptContext* scriptContext, Js::Var* pArgumentsObject) const
  471. {
  472. Assert(globalBailOutRecordTable->hasStackArgOpt);
  473. if (PHASE_OFF1(Js::StackArgFormalsOptPhase))
  474. {
  475. newInstance->OP_LdHeapArguments(scriptContext);
  476. }
  477. else
  478. {
  479. newInstance->CreateEmptyHeapArgumentsObject(scriptContext);
  480. }
  481. Assert(newInstance->m_arguments);
  482. *pArgumentsObject = (Js::ArgumentsObject*)newInstance->m_arguments;
  483. return newInstance->m_arguments;
  484. }
  485. Js::JavascriptCallStackLayout *BailOutRecord::GetStackLayout() const
  486. {
  487. return
  488. Js::JavascriptCallStackLayout::FromFramePointer(
  489. globalBailOutRecordTable->registerSaveSpace[LinearScanMD::GetRegisterSaveIndex(LowererMD::GetRegFramePointer()) - 1]);
  490. }
  491. void
  492. BailOutRecord::RestoreValues(IR::BailOutKind bailOutKind, Js::JavascriptCallStackLayout * layout, Js::InterpreterStackFrame * newInstance,
  493. Js::ScriptContext * scriptContext, bool fromLoopBody, Js::Var * registerSaves, BailOutReturnValue * bailOutReturnValue, Js::Var* pArgumentsObject,
  494. Js::Var branchValue, void * returnAddress, bool useStartCall /* = true */, void * argoutRestoreAddress) const
  495. {
  496. Js::AutoPushReturnAddressForStackWalker saveReturnAddress(scriptContext, returnAddress);
  497. if (this->stackLiteralBailOutRecordCount)
  498. {
  499. // Null out the field on the stack literal that hasn't fully initialized yet.
  500. globalBailOutRecordTable->IterateGlobalBailOutRecordTableRows(m_bailOutRecordId, [=](GlobalBailOutRecordDataRow *row)
  501. {
  502. for (uint i = 0; i < this->stackLiteralBailOutRecordCount; i++)
  503. {
  504. BailOutRecord::StackLiteralBailOutRecord& record = this->stackLiteralBailOutRecord[i];
  505. if (record.regSlot == row->regSlot)
  506. {
  507. // Partially initialized stack literal shouldn't be type specialized yet.
  508. Assert(!row->isFloat);
  509. Assert(!row->isInt);
  510. int offset = row->offset;
  511. Js::Var value;
  512. if (offset < 0)
  513. {
  514. // Stack offset
  515. value = layout->GetOffset(offset);
  516. }
  517. else
  518. {
  519. // The value is in register
  520. // Index is one based, so subtract one
  521. Assert((uint)offset <= GetBailOutRegisterSaveSlotCount());
  522. Js::Var * registerSaveSpace = registerSaves ? registerSaves : (Js::Var *)scriptContext->GetThreadContext()->GetBailOutRegisterSaveSpace();
  523. Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] != TyFloat64);
  524. value = registerSaveSpace[offset - 1];
  525. }
  526. Assert(Js::DynamicObject::Is(value));
  527. Assert(ThreadContext::IsOnStack(value));
  528. Js::DynamicObject * obj = Js::DynamicObject::FromVar(value);
  529. uint propertyCount = obj->GetPropertyCount();
  530. for (uint j = record.initFldCount; j < propertyCount; j++)
  531. {
  532. obj->SetSlot(SetSlotArgumentsRoot(Js::Constants::NoProperty, false, j, nullptr));
  533. }
  534. }
  535. }
  536. });
  537. }
  538. if (this->localOffsetsCount)
  539. {
  540. Js::FunctionBody* functionBody = newInstance->function->GetFunctionBody();
  541. #if ENABLE_DEBUG_CONFIG_OPTIONS
  542. BAILOUT_VERBOSE_TRACE(functionBody, bailOutKind, _u("BailOut: Register #%3d: Not live\n"), 0);
  543. for (uint i = 1; i < functionBody->GetConstantCount(); i++)
  544. {
  545. BAILOUT_VERBOSE_TRACE(functionBody, bailOutKind, _u("BailOut: Register #%3d: Constant table\n"), i);
  546. }
  547. #endif
  548. if (functionBody->IsInDebugMode())
  549. {
  550. this->AdjustOffsetsForDiagMode(layout, newInstance->GetJavascriptFunction());
  551. }
  552. this->RestoreValues(bailOutKind, layout, this->localOffsetsCount,
  553. nullptr, 0, newInstance->m_localSlots, scriptContext, fromLoopBody, registerSaves, newInstance, pArgumentsObject);
  554. }
  555. if (useStartCall && this->argOutOffsetInfo)
  556. {
  557. uint outParamSlot = 0;
  558. void * argRestoreAddr = nullptr;
  559. for (uint i = 0; i < this->argOutOffsetInfo->startCallCount; i++)
  560. {
  561. uint startCallOutParamCount = this->argOutOffsetInfo->startCallOutParamCounts[i];
  562. #ifdef _M_IX86
  563. if (argoutRestoreAddress)
  564. {
  565. argRestoreAddr = (void*)((char*)argoutRestoreAddress + (this->startCallArgRestoreAdjustCounts[i] * MachPtr));
  566. }
  567. #endif
  568. newInstance->OP_StartCall(startCallOutParamCount);
  569. this->RestoreValues(bailOutKind, layout, startCallOutParamCount, &this->argOutOffsetInfo->outParamOffsets[outParamSlot],
  570. this->argOutOffsetInfo->argOutSymStart + outParamSlot, newInstance->m_outParams,
  571. scriptContext, fromLoopBody, registerSaves, newInstance, pArgumentsObject, argRestoreAddr);
  572. outParamSlot += startCallOutParamCount;
  573. }
  574. }
  575. // If we're not in a loop body, then the arguments object is not on the local frame.
  576. // If the RestoreValues created an arguments object for us, then it's already on the interpreter instance.
  577. // Otherwise, we need to propagate the object from the jitted frame to the interpreter.
  578. Assert(newInstance->function && newInstance->function->GetFunctionBody());
  579. bool hasArgumentSlot = // Be consistent with Func::HasArgumentSlot.
  580. !fromLoopBody && newInstance->function->GetFunctionBody()->GetInParamsCount() != 0;
  581. if (hasArgumentSlot && newInstance->m_arguments == nullptr)
  582. {
  583. newInstance->m_arguments = *pArgumentsObject;
  584. }
  585. if (bailOutReturnValue != nullptr && bailOutReturnValue->returnValueRegSlot != Js::Constants::NoRegister)
  586. {
  587. Assert(bailOutReturnValue->returnValue != nullptr);
  588. Assert(bailOutReturnValue->returnValueRegSlot < newInstance->GetJavascriptFunction()->GetFunctionBody()->GetLocalsCount());
  589. newInstance->m_localSlots[bailOutReturnValue->returnValueRegSlot] = bailOutReturnValue->returnValue;
  590. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("BailOut: Register #%3d: Return, value: 0x%p\n"),
  591. bailOutReturnValue->returnValueRegSlot, bailOutReturnValue->returnValue);
  592. }
  593. if (branchValueRegSlot != Js::Constants::NoRegister)
  594. {
  595. // Used when a t1 = CmCC is optimize to BrCC, and the branch bails out. T1 needs to be restored
  596. Assert(branchValue && Js::JavascriptBoolean::Is(branchValue));
  597. Assert(branchValueRegSlot < newInstance->GetJavascriptFunction()->GetFunctionBody()->GetLocalsCount());
  598. newInstance->m_localSlots[branchValueRegSlot] = branchValue;
  599. }
  600. #if DBG
  601. // Clear the register save area for the next bailout
  602. memset((void*)scriptContext->GetThreadContext()->GetBailOutRegisterSaveSpace(), 0, GetBailOutRegisterSaveSlotCount() * sizeof(Js::Var));
  603. #endif
  604. }
  605. void
  606. BailOutRecord::AdjustOffsetsForDiagMode(Js::JavascriptCallStackLayout * layout, Js::ScriptFunction * function) const
  607. {
  608. // In this function we are going to do
  609. // 1. Check if the value got changed (by checking at the particular location at the stack)
  610. // 2. In that case update the offset to point to the stack offset
  611. Js::FunctionBody *functionBody = function->GetFunctionBody();
  612. Assert(functionBody != nullptr);
  613. Assert(functionBody->IsInDebugMode());
  614. Js::FunctionEntryPointInfo *entryPointInfo = functionBody->GetDefaultFunctionEntryPointInfo();
  615. Assert(entryPointInfo != nullptr);
  616. // Note: the offset may be not initialized/InvalidOffset when there are no non-temp local vars.
  617. if (entryPointInfo->localVarChangedOffset != Js::Constants::InvalidOffset)
  618. {
  619. Assert(functionBody->GetNonTempLocalVarCount() != 0);
  620. char * valueChangeOffset = layout->GetValueChangeOffset(entryPointInfo->localVarChangedOffset);
  621. if (*valueChangeOffset == Js::FunctionBody::LocalsChangeDirtyValue)
  622. {
  623. // The value got changed due to debugger, lets read values from the stack position
  624. // Get the corresponding offset on the stack related to the frame.
  625. globalBailOutRecordTable->IterateGlobalBailOutRecordTableRows(m_bailOutRecordId, [=](GlobalBailOutRecordDataRow *row) {
  626. int32 offset = row->offset;
  627. // offset is zero, is it possible that a locals is not living in the debug mode?
  628. Assert(offset != 0);
  629. int32 slotOffset;
  630. if (functionBody->GetSlotOffset(row->regSlot, &slotOffset))
  631. {
  632. slotOffset = entryPointInfo->localVarSlotsOffset + slotOffset;
  633. // If it was taken from the stack location, we should have arrived to the same stack location.
  634. Assert(offset > 0 || offset == slotOffset);
  635. row->offset = slotOffset;
  636. }
  637. });
  638. }
  639. }
  640. }
  641. void
  642. BailOutRecord::IsOffsetNativeIntOrFloat(uint offsetIndex, int argOutSlotStart, bool * pIsFloat64, bool * pIsInt32,
  643. bool * pIsSimd128F4, bool * pIsSimd128I4, bool * pIsSimd128I8, bool * pIsSimd128I16, bool * pIsSimd128U4,
  644. bool * pIsSimd128U8, bool * pIsSimd128U16, bool * pIsSimd128B4, bool * pIsSimd128B8, bool * pIsSimd128B16) const
  645. {
  646. bool isFloat64 = this->argOutOffsetInfo->argOutFloat64Syms->Test(argOutSlotStart + offsetIndex) != 0;
  647. bool isInt32 = this->argOutOffsetInfo->argOutLosslessInt32Syms->Test(argOutSlotStart + offsetIndex) != 0;
  648. #ifdef ENABLE_SIMDJS
  649. // SIMD_JS
  650. bool isSimd128F4 = this->argOutOffsetInfo->argOutSimd128F4Syms->Test(argOutSlotStart + offsetIndex) != 0;
  651. bool isSimd128I4 = this->argOutOffsetInfo->argOutSimd128I4Syms->Test(argOutSlotStart + offsetIndex) != 0;
  652. bool isSimd128I8 = this->argOutOffsetInfo->argOutSimd128I8Syms->Test(argOutSlotStart + offsetIndex) != 0;
  653. bool isSimd128I16 = this->argOutOffsetInfo->argOutSimd128I16Syms->Test(argOutSlotStart + offsetIndex) != 0;
  654. bool isSimd128U4 = this->argOutOffsetInfo->argOutSimd128U4Syms->Test(argOutSlotStart + offsetIndex) != 0;
  655. bool isSimd128U8 = this->argOutOffsetInfo->argOutSimd128U8Syms->Test(argOutSlotStart + offsetIndex) != 0;
  656. bool isSimd128U16 = this->argOutOffsetInfo->argOutSimd128U16Syms->Test(argOutSlotStart + offsetIndex) != 0;
  657. bool isSimd128B4 = this->argOutOffsetInfo->argOutSimd128B4Syms->Test(argOutSlotStart + offsetIndex) != 0;
  658. bool isSimd128B8 = this->argOutOffsetInfo->argOutSimd128B8Syms->Test(argOutSlotStart + offsetIndex) != 0;
  659. bool isSimd128B16 = this->argOutOffsetInfo->argOutSimd128B16Syms->Test(argOutSlotStart + offsetIndex) != 0;
  660. #endif
  661. #ifdef ENABLE_SIMDJS
  662. Assert(!isFloat64 || !isInt32 ||
  663. !isSimd128F4 || !isSimd128I4 || !isSimd128I8 || !isSimd128I16 || !
  664. !isSimd128U4 || !isSimd128U8 || !isSimd128U16);
  665. #else
  666. Assert(!isFloat64 || !isInt32);
  667. #endif
  668. *pIsFloat64 = isFloat64;
  669. *pIsInt32 = isInt32;
  670. #ifdef ENABLE_SIMDJS
  671. *pIsSimd128F4 = isSimd128F4;
  672. *pIsSimd128I4 = isSimd128I4;
  673. *pIsSimd128I8 = isSimd128I8;
  674. *pIsSimd128I16 = isSimd128I16;
  675. *pIsSimd128U4 = isSimd128U4;
  676. *pIsSimd128U8 = isSimd128U8;
  677. *pIsSimd128U16 = isSimd128U16;
  678. *pIsSimd128B4 = isSimd128B4;
  679. *pIsSimd128B8 = isSimd128B8;
  680. *pIsSimd128B16 = isSimd128B16;
  681. #else
  682. *pIsSimd128F4 = false;
  683. *pIsSimd128I4 = false;
  684. *pIsSimd128I8 = false;
  685. *pIsSimd128I16 = false;
  686. *pIsSimd128U4 = false;
  687. *pIsSimd128U8 = false;
  688. *pIsSimd128U16 = false;
  689. *pIsSimd128B4 = false;
  690. *pIsSimd128B8 = false;
  691. *pIsSimd128B16 = false;
  692. #endif
  693. }
  694. void
  695. BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStackLayout * layout, Js::Var * values, Js::ScriptContext * scriptContext,
  696. bool fromLoopBody, Js::Var * registerSaves, Js::InterpreterStackFrame * newInstance, Js::Var* pArgumentsObject, void * argoutRestoreAddress,
  697. uint regSlot, int offset, bool isLocal, bool isFloat64, bool isInt32,
  698. bool isSimd128F4, bool isSimd128I4, bool isSimd128I8, bool isSimd128I16,
  699. bool isSimd128U4, bool isSimd128U8, bool isSimd128U16, bool isSimd128B4, bool isSimd128B8, bool isSimd128B16) const
  700. {
  701. bool boxStackInstance = true;
  702. Js::Var value = 0;
  703. double dblValue = 0.0;
  704. int32 int32Value = 0;
  705. SIMDValue simdValue = { 0, 0, 0, 0 };
  706. #if ENABLE_DEBUG_CONFIG_OPTIONS
  707. char16 const * name = _u("OutParam");
  708. if (isLocal)
  709. {
  710. name = _u("Register");
  711. }
  712. #endif
  713. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("BailOut: %s #%3d: "), name, regSlot);
  714. if (offset < 0)
  715. {
  716. // Stack offset are negative
  717. if (!argoutRestoreAddress)
  718. {
  719. if (isFloat64)
  720. {
  721. dblValue = layout->GetDoubleAtOffset(offset);
  722. }
  723. else if (isInt32)
  724. {
  725. int32Value = layout->GetInt32AtOffset(offset);
  726. }
  727. else if (
  728. isSimd128F4 || isSimd128I4 || isSimd128I8 || isSimd128I16 ||
  729. isSimd128U4 || isSimd128U8 || isSimd128U16 || isSimd128B4 ||
  730. isSimd128B8 || isSimd128B16
  731. )
  732. {
  733. // SIMD_JS
  734. simdValue = layout->GetSimdValueAtOffset(offset);
  735. }
  736. else
  737. {
  738. value = layout->GetOffset(offset);
  739. AssertMsg(!(newInstance->function->GetFunctionBody()->IsInDebugMode() &&
  740. newInstance->function->GetFunctionBody()->IsNonTempLocalVar(regSlot) &&
  741. value == (Js::Var)Func::c_debugFillPattern),
  742. "Uninitialized value (debug mode only)? Try -trace:bailout -verbose and check last traced reg in byte code.");
  743. }
  744. }
  745. else if (!isLocal)
  746. {
  747. // If we have:
  748. // try {
  749. // bar(a, b, c);
  750. // } catch(..) {..}
  751. // and we bailout during bar args evaluation, we recover from args from argoutRestoreAddress, not from caller function frame.
  752. // This is because try-catch is implemented as a C wrapper, so args will be a different offset from rbp in that case.
  753. Assert(
  754. !isFloat64 && !isInt32 &&
  755. !isSimd128F4 && !isSimd128I4 && !isSimd128I8 && !isSimd128I16 && !isSimd128U4 &&
  756. !isSimd128U8 && !isSimd128U16 && !isSimd128B4 && !isSimd128B8 && !isSimd128B16
  757. );
  758. value = *((Js::Var *)(((char *)argoutRestoreAddress) + regSlot * MachPtr));
  759. AssertMsg(!(newInstance->function->GetFunctionBody()->IsInDebugMode() &&
  760. newInstance->function->GetFunctionBody()->IsNonTempLocalVar(regSlot) &&
  761. value == (Js::Var)Func::c_debugFillPattern),
  762. "Uninitialized value (debug mode only)? Try -trace:bailout -verbose and check last traced reg in byte code.");
  763. }
  764. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Stack offset %6d"), offset);
  765. }
  766. else if (offset > 0)
  767. {
  768. if ((uint)offset <= GetBailOutRegisterSaveSlotCount())
  769. {
  770. // Register save space (offset is the register number and index into the register save space)
  771. // Index is one based, so subtract one
  772. AssertOrFailFast(registerSaves);
  773. if (isFloat64)
  774. {
  775. Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] == TyFloat64);
  776. dblValue = *((double*)&(registerSaves[offset - 1]));
  777. #ifdef _M_ARM
  778. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Register %-4S %4d"), RegNames[(offset - RegD0) / 2 + RegD0], offset);
  779. #else
  780. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Register %-4S %4d"), RegNames[LinearScanMD::GetRegisterFromSaveIndex(offset)], offset);
  781. #endif
  782. }
  783. else
  784. {
  785. if (
  786. isSimd128F4 || isSimd128I4 || isSimd128I8 || isSimd128I16 ||
  787. isSimd128U4 || isSimd128U8 || isSimd128U16 || isSimd128B4 ||
  788. isSimd128B8 || isSimd128B16
  789. )
  790. {
  791. simdValue = *((SIMDValue *)&(registerSaves[offset - 1]));
  792. }
  793. else if (isInt32)
  794. {
  795. Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] != TyFloat64);
  796. int32Value = ::Math::PointerCastToIntegralTruncate<int32>(registerSaves[offset - 1]);
  797. }
  798. else
  799. {
  800. Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] != TyFloat64);
  801. value = registerSaves[offset - 1];
  802. }
  803. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Register %-4S %4d"), RegNames[LinearScanMD::GetRegisterFromSaveIndex(offset)], offset);
  804. }
  805. }
  806. else if (BailOutRecord::IsArgumentsObject((uint)offset))
  807. {
  808. Assert(!isFloat64);
  809. Assert(!isInt32);
  810. Assert(!fromLoopBody);
  811. value = *pArgumentsObject;
  812. if (value == nullptr)
  813. {
  814. value = EnsureArguments(newInstance, layout, scriptContext, pArgumentsObject);
  815. }
  816. Assert(value);
  817. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Arguments object"));
  818. boxStackInstance = false;
  819. }
  820. else
  821. {
  822. // Constants offset starts from max bail out register save slot count;
  823. uint constantIndex = offset - (GetBailOutRegisterSaveSlotCount() + GetBailOutReserveSlotCount()) - 1;
  824. if (isInt32)
  825. {
  826. int32Value = ::Math::PointerCastToIntegralTruncate<int32>(this->constants[constantIndex]);
  827. }
  828. else
  829. {
  830. value = this->constants[constantIndex];
  831. }
  832. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Constant index %4d"), constantIndex);
  833. boxStackInstance = false;
  834. }
  835. }
  836. else
  837. {
  838. // Consider Assert(false) here
  839. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Not live\n"));
  840. return;
  841. }
  842. if (isFloat64)
  843. {
  844. value = Js::JavascriptNumber::New(dblValue, scriptContext);
  845. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: %f (ToVar: 0x%p)"), dblValue, value);
  846. }
  847. else if (isInt32)
  848. {
  849. Assert(!value);
  850. value = Js::JavascriptNumber::ToVar(int32Value, scriptContext);
  851. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: %10d (ToVar: 0x%p)"), int32Value, value);
  852. }
  853. #ifdef ENABLE_SIMDJS
  854. // SIMD_JS
  855. else if (isSimd128F4)
  856. {
  857. Assert(!value);
  858. value = Js::JavascriptSIMDFloat32x4::New(&simdValue, scriptContext);
  859. }
  860. else if (isSimd128I4)
  861. {
  862. Assert(!value);
  863. value = Js::JavascriptSIMDInt32x4::New(&simdValue, scriptContext);
  864. }
  865. else if (isSimd128I8)
  866. {
  867. Assert(!value);
  868. value = Js::JavascriptSIMDInt16x8::New(&simdValue, scriptContext);
  869. }
  870. else if (isSimd128I16)
  871. {
  872. Assert(!value);
  873. value = Js::JavascriptSIMDInt8x16::New(&simdValue, scriptContext);
  874. }
  875. else if (isSimd128U4)
  876. {
  877. Assert(!value);
  878. value = Js::JavascriptSIMDUint32x4::New(&simdValue, scriptContext);
  879. }
  880. else if (isSimd128U8)
  881. {
  882. Assert(!value);
  883. value = Js::JavascriptSIMDUint16x8::New(&simdValue, scriptContext);
  884. }
  885. else if (isSimd128U16)
  886. {
  887. Assert(!value);
  888. value = Js::JavascriptSIMDUint8x16::New(&simdValue, scriptContext);
  889. }
  890. else if (isSimd128B4)
  891. {
  892. Assert(!value);
  893. value = Js::JavascriptSIMDBool32x4::New(&simdValue, scriptContext);
  894. }
  895. else if (isSimd128B8)
  896. {
  897. Assert(!value);
  898. value = Js::JavascriptSIMDBool16x8::New(&simdValue, scriptContext);
  899. }
  900. else if (isSimd128B16)
  901. {
  902. Assert(!value);
  903. value = Js::JavascriptSIMDBool8x16::New(&simdValue, scriptContext);
  904. }
  905. #endif // #ifdef ENABLE_SIMDJS
  906. else
  907. {
  908. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: 0x%p"), value);
  909. if (boxStackInstance)
  910. {
  911. Js::Var oldValue = value;
  912. value = Js::JavascriptOperators::BoxStackInstance(oldValue, scriptContext, /* allowStackFunction */ true);
  913. if (oldValue != value)
  914. {
  915. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(" (Boxed: 0x%p)"), value);
  916. }
  917. }
  918. }
  919. values[regSlot] = value;
  920. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("\n"));
  921. }
  922. void
  923. BailOutRecord::RestoreValues(IR::BailOutKind bailOutKind, Js::JavascriptCallStackLayout * layout, uint count, __in_ecount_opt(count) int * offsets, int argOutSlotStart,
  924. __out_ecount(count) Js::Var * values, Js::ScriptContext * scriptContext,
  925. bool fromLoopBody, Js::Var * registerSaves, Js::InterpreterStackFrame * newInstance, Js::Var* pArgumentsObject, void * argoutRestoreAddress) const
  926. {
  927. bool isLocal = offsets == nullptr;
  928. if (isLocal == true)
  929. {
  930. globalBailOutRecordTable->IterateGlobalBailOutRecordTableRows(m_bailOutRecordId, [=](GlobalBailOutRecordDataRow *row) {
  931. Assert(row->offset != 0);
  932. #ifdef ENABLE_SIMDJS
  933. RestoreValue(bailOutKind, layout, values, scriptContext, fromLoopBody, registerSaves, newInstance, pArgumentsObject,
  934. argoutRestoreAddress, row->regSlot, row->offset, true, row->isFloat, row->isInt, row->isSimd128F4,
  935. row->isSimd128I4, row->isSimd128I8, row->isSimd128I16, row->isSimd128U4, row->isSimd128U8, row->isSimd128U16,
  936. row->isSimd128B4, row->isSimd128B8, row->isSimd128B16);
  937. #else
  938. // https://github.com/Microsoft/ChakraCore/issues/3274
  939. // Change signature of RestoreValue if WASM simd doesn't use this
  940. RestoreValue(bailOutKind, layout, values, scriptContext, fromLoopBody, registerSaves, newInstance, pArgumentsObject,
  941. argoutRestoreAddress, row->regSlot, row->offset, true, row->isFloat, row->isInt, false,
  942. false, false, false, false, false, false,
  943. false, false, false);
  944. #endif
  945. });
  946. }
  947. else
  948. {
  949. for (uint i = 0; i < count; i++)
  950. {
  951. int offset = 0;
  952. // The variables below determine whether we have a Var or native float/int.
  953. bool isFloat64;
  954. bool isInt32;
  955. bool isSimd128F4;
  956. bool isSimd128I4;
  957. bool isSimd128I8;
  958. bool isSimd128I16;
  959. bool isSimd128U4;
  960. bool isSimd128U8;
  961. bool isSimd128U16;
  962. bool isSimd128B4;
  963. bool isSimd128B8;
  964. bool isSimd128B16;
  965. offset = offsets[i];
  966. this->IsOffsetNativeIntOrFloat(i, argOutSlotStart, &isFloat64, &isInt32,
  967. &isSimd128F4, &isSimd128I4, &isSimd128I8, &isSimd128I16,
  968. &isSimd128U4, &isSimd128U8, &isSimd128U16, &isSimd128B4, &isSimd128B8, &isSimd128B16);
  969. RestoreValue(bailOutKind, layout, values, scriptContext, fromLoopBody, registerSaves, newInstance, pArgumentsObject,
  970. argoutRestoreAddress, i, offset, false, isFloat64, isInt32, isSimd128F4, isSimd128I4, isSimd128I8,
  971. isSimd128I16, isSimd128U4, isSimd128U8, isSimd128U16, isSimd128B4, isSimd128B8, isSimd128B16);
  972. }
  973. }
  974. }
  975. Js::Var BailOutRecord::BailOut(BailOutRecord const * bailOutRecord)
  976. {
  977. Assert(bailOutRecord);
  978. void * argoutRestoreAddr = nullptr;
  979. #ifdef _M_IX86
  980. void * addressOfRetAddress = _AddressOfReturnAddress();
  981. if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0 || bailOutRecord->ehBailoutData->finallyOffset != 0 || bailOutRecord->ehBailoutData->ht == Js::HandlerType::HT_Finally))
  982. {
  983. // For a bailout in argument evaluation from an EH region, the esp is offset by the TryCatch helper's frame. So, the argouts are not at the offsets
  984. // stored in the bailout record, which are relative to ebp. Need to restore the argouts from the actual value of esp before calling the Bailout helper
  985. argoutRestoreAddr = (void *)((char*)addressOfRetAddress + ((1 + 1) * MachPtr)); // Account for the parameter and return address of this function
  986. }
  987. #endif
  988. Js::JavascriptCallStackLayout *const layout = bailOutRecord->GetStackLayout();
  989. Js::ScriptFunction * function = (Js::ScriptFunction *)layout->functionObject;
  990. Assert(function->GetScriptContext()->GetThreadContext()->GetPendingFinallyException() == nullptr ||
  991. bailOutRecord->bailOutKind == IR::BailOutOnException);
  992. if (bailOutRecord->bailOutKind == IR::BailOutOnImplicitCalls)
  993. {
  994. function->GetScriptContext()->GetThreadContext()->CheckAndResetImplicitCallAccessorFlag();
  995. }
  996. Js::ImplicitCallFlags savedImplicitCallFlags = function->GetScriptContext()->GetThreadContext()->GetImplicitCallFlags();
  997. if(bailOutRecord->globalBailOutRecordTable->isLoopBody)
  998. {
  999. if (bailOutRecord->globalBailOutRecordTable->isInlinedFunction)
  1000. {
  1001. return reinterpret_cast<Js::Var>(BailOutFromLoopBodyInlined(layout, bailOutRecord, _ReturnAddress()));
  1002. }
  1003. return reinterpret_cast<Js::Var>(BailOutFromLoopBody(layout, bailOutRecord));
  1004. }
  1005. if(bailOutRecord->globalBailOutRecordTable->isInlinedFunction)
  1006. {
  1007. return BailOutInlined(layout, bailOutRecord, _ReturnAddress(), savedImplicitCallFlags);
  1008. }
  1009. return BailOutFromFunction(layout, bailOutRecord, _ReturnAddress(), argoutRestoreAddr, savedImplicitCallFlags);
  1010. }
  1011. uint32
  1012. BailOutRecord::BailOutFromLoopBody(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord)
  1013. {
  1014. Assert(bailOutRecord->parent == nullptr);
  1015. return BailOutFromLoopBodyCommon(layout, bailOutRecord, bailOutRecord->bailOutOffset, bailOutRecord->bailOutKind);
  1016. }
  1017. Js::Var
  1018. BailOutRecord::BailOutFromFunction(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord, void * returnAddress, void * argoutRestoreAddress, Js::ImplicitCallFlags savedImplicitCallFlags)
  1019. {
  1020. Assert(bailOutRecord->parent == nullptr);
  1021. return BailOutCommon(layout, bailOutRecord, bailOutRecord->bailOutOffset, returnAddress, bailOutRecord->bailOutKind, savedImplicitCallFlags, nullptr, nullptr, argoutRestoreAddress);
  1022. }
  1023. Js::Var
  1024. BailOutRecord::BailOutInlined(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord, void * returnAddress, Js::ImplicitCallFlags savedImplicitCallFlags)
  1025. {
  1026. Assert(bailOutRecord->parent != nullptr);
  1027. return BailOutInlinedCommon(layout, bailOutRecord, bailOutRecord->bailOutOffset, returnAddress, bailOutRecord->bailOutKind, savedImplicitCallFlags);
  1028. }
  1029. uint32
  1030. BailOutRecord::BailOutFromLoopBodyInlined(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord, void * returnAddress)
  1031. {
  1032. Assert(bailOutRecord->parent != nullptr);
  1033. return BailOutFromLoopBodyInlinedCommon(layout, bailOutRecord, bailOutRecord->bailOutOffset, returnAddress, bailOutRecord->bailOutKind);
  1034. }
  1035. Js::Var
  1036. BailOutRecord::BailOutCommonNoCodeGen(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
  1037. uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Var branchValue, Js::Var * registerSaves,
  1038. BailOutReturnValue * bailOutReturnValue, void * argoutRestoreAddress)
  1039. {
  1040. Assert(bailOutRecord->parent == nullptr);
  1041. Assert(Js::ScriptFunction::Is(layout->functionObject));
  1042. Js::ScriptFunction ** functionRef = (Js::ScriptFunction **)&layout->functionObject;
  1043. Js::ArgumentReader args(&layout->callInfo, layout->args);
  1044. Js::Var result = BailOutHelper(layout, functionRef, args, false, bailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, bailOutReturnValue, layout->GetArgumentsObjectLocation(), branchValue, argoutRestoreAddress);
  1045. return result;
  1046. }
  1047. Js::Var
  1048. BailOutRecord::BailOutCommon(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
  1049. uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::ImplicitCallFlags savedImplicitCallFlags, Js::Var branchValue, BailOutReturnValue * bailOutReturnValue, void * argoutRestoreAddress)
  1050. {
  1051. // Do not remove the following code.
  1052. // Need to capture the int registers on stack as threadContext->bailOutRegisterSaveSpace is allocated from ThreadAlloc and is not scanned by recycler.
  1053. // We don't want to save float (xmm) registers as they can be huge and they cannot contain a var.
  1054. // However, we're somewhat stuck. We need to keep the references around until we restore values, but
  1055. // we don't have a use for them. The easiest solution is to simply pass this into the corresponding
  1056. // parameter for BailOutcommonNoCodeGen, but that requires us to save all of the vars, not just the
  1057. // int ones. This is ultimately significantly more predictable than attempting to manage the lifetimes
  1058. // in some other way though. We can't just do what we were doing previously, which is saving values
  1059. // here and not passing them into BailOutCommonNoCodeGen, because then the compiler will likely get
  1060. // rid of the memcpy and then the dead registerSaves array, since it can figure out that there's no
  1061. // side effect (due to the GC not being something that the optimizer can, or should, reason about).
  1062. Js::Var registerSaves[BailOutRegisterSaveSlotCount];
  1063. js_memcpy_s(registerSaves, sizeof(registerSaves), (Js::Var *)layout->functionObject->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace(),
  1064. sizeof(registerSaves));
  1065. Js::Var result = BailOutCommonNoCodeGen(layout, bailOutRecord, bailOutOffset, returnAddress, bailOutKind, branchValue, registerSaves, bailOutReturnValue, argoutRestoreAddress);
  1066. ScheduleFunctionCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), nullptr, bailOutRecord, bailOutKind, bailOutOffset, savedImplicitCallFlags, returnAddress);
  1067. return result;
  1068. }
  1069. Js::Var
  1070. BailOutRecord::BailOutInlinedCommon(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord, uint32 bailOutOffset,
  1071. void * returnAddress, IR::BailOutKind bailOutKind, Js::ImplicitCallFlags savedImplicitCallFlags, Js::Var branchValue)
  1072. {
  1073. Assert(bailOutRecord->parent != nullptr);
  1074. // Need to capture the register save, one of the bailout might get into jitted code again and bailout again
  1075. // overwriting the current register saves
  1076. Js::Var registerSaves[BailOutRegisterSaveSlotCount];
  1077. js_memcpy_s(registerSaves, sizeof(registerSaves), (Js::Var *)layout->functionObject->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace(),
  1078. sizeof(registerSaves));
  1079. BailOutRecord const * currentBailOutRecord = bailOutRecord;
  1080. BailOutReturnValue bailOutReturnValue;
  1081. Js::ScriptFunction * innerMostInlinee = nullptr;
  1082. BailOutInlinedHelper(layout, currentBailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, &bailOutReturnValue, &innerMostInlinee, false, branchValue);
  1083. Js::Var result = BailOutCommonNoCodeGen(layout, currentBailOutRecord, currentBailOutRecord->bailOutOffset, returnAddress, bailOutKind, branchValue,
  1084. registerSaves, &bailOutReturnValue);
  1085. ScheduleFunctionCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), innerMostInlinee, currentBailOutRecord, bailOutKind, bailOutOffset, savedImplicitCallFlags, returnAddress);
  1086. return result;
  1087. }
  1088. uint32
  1089. BailOutRecord::BailOutFromLoopBodyCommon(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord, uint32 bailOutOffset,
  1090. IR::BailOutKind bailOutKind, Js::Var branchValue)
  1091. {
  1092. // This isn't strictly necessary if there's no allocations on this path, but because such an
  1093. // issue would be hard to notice and introduce some significant issues, we can do this copy.
  1094. // The problem from not doing this and then doing an allocation before RestoreValues is that
  1095. // the GC doesn't check the BailOutRegisterSaveSpace.
  1096. Js::Var registerSaves[BailOutRegisterSaveSlotCount];
  1097. js_memcpy_s(registerSaves, sizeof(registerSaves), (Js::Var *)layout->functionObject->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace(),
  1098. sizeof(registerSaves));
  1099. uint32 result = BailOutFromLoopBodyHelper(layout, bailOutRecord, bailOutOffset, bailOutKind, branchValue, registerSaves);
  1100. ScheduleLoopBodyCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), nullptr, bailOutRecord, bailOutKind);
  1101. return result;
  1102. }
  1103. uint32
  1104. BailOutRecord::BailOutFromLoopBodyInlinedCommon(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
  1105. uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Var branchValue)
  1106. {
  1107. Assert(bailOutRecord->parent != nullptr);
  1108. // This isn't strictly necessary if there's no allocations on this path, but because such an
  1109. // issue would be hard to notice and introduce some significant issues, we can do this copy.
  1110. // The problem from not doing this and then doing an allocation before RestoreValues is that
  1111. // the GC doesn't check the BailOutRegisterSaveSpace.
  1112. Js::Var registerSaves[BailOutRegisterSaveSlotCount];
  1113. js_memcpy_s(registerSaves, sizeof(registerSaves), (Js::Var *)layout->functionObject->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace(),
  1114. sizeof(registerSaves));
  1115. BailOutRecord const * currentBailOutRecord = bailOutRecord;
  1116. BailOutReturnValue bailOutReturnValue;
  1117. Js::ScriptFunction * innerMostInlinee = nullptr;
  1118. BailOutInlinedHelper(layout, currentBailOutRecord, bailOutOffset, returnAddress, bailOutKind, registerSaves, &bailOutReturnValue, &innerMostInlinee, true, branchValue);
  1119. uint32 result = BailOutFromLoopBodyHelper(layout, currentBailOutRecord, currentBailOutRecord->bailOutOffset,
  1120. bailOutKind, nullptr, registerSaves, &bailOutReturnValue);
  1121. ScheduleLoopBodyCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), innerMostInlinee, currentBailOutRecord, bailOutKind);
  1122. return result;
  1123. }
  1124. void
  1125. BailOutRecord::BailOutInlinedHelper(Js::JavascriptCallStackLayout * layout, BailOutRecord const *& currentBailOutRecord,
  1126. uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Var * registerSaves, BailOutReturnValue * bailOutReturnValue, Js::ScriptFunction ** innerMostInlinee, bool isInLoopBody, Js::Var branchValue)
  1127. {
  1128. Assert(currentBailOutRecord->parent != nullptr);
  1129. BailOutReturnValue * lastBailOutReturnValue = nullptr;
  1130. *innerMostInlinee = nullptr;
  1131. Js::FunctionBody* functionBody = Js::ScriptFunction::FromVar(layout->functionObject)->GetFunctionBody();
  1132. Js::EntryPointInfo *entryPointInfo;
  1133. if(isInLoopBody)
  1134. {
  1135. Js::InterpreterStackFrame * interpreterFrame = functionBody->GetScriptContext()->GetThreadContext()->GetLeafInterpreterFrame();
  1136. uint loopNum = interpreterFrame->GetCurrentLoopNum();
  1137. entryPointInfo = (Js::EntryPointInfo*)functionBody->GetLoopEntryPointInfoFromNativeAddress((DWORD_PTR)returnAddress, loopNum);
  1138. }
  1139. else
  1140. {
  1141. entryPointInfo = (Js::EntryPointInfo*)functionBody->GetEntryPointFromNativeAddress((DWORD_PTR)returnAddress);
  1142. }
  1143. // Let's restore the inline stack - so that in case of a stack walk we have it available
  1144. if (entryPointInfo->HasInlinees())
  1145. {
  1146. InlineeFrameRecord* inlineeFrameRecord = entryPointInfo->FindInlineeFrame(returnAddress);
  1147. if (inlineeFrameRecord)
  1148. {
  1149. InlinedFrameLayout* outerMostFrame = (InlinedFrameLayout *)(((uint8 *)Js::JavascriptCallStackLayout::ToFramePointer(layout)) - entryPointInfo->frameHeight);
  1150. inlineeFrameRecord->RestoreFrames(functionBody, outerMostFrame, layout);
  1151. }
  1152. }
  1153. do
  1154. {
  1155. InlinedFrameLayout *inlinedFrame = (InlinedFrameLayout *)(((char *)layout) + currentBailOutRecord->globalBailOutRecordTable->firstActualStackOffset);
  1156. Js::InlineeCallInfo inlineeCallInfo = inlinedFrame->callInfo;
  1157. Assert((Js::ArgSlot)inlineeCallInfo.Count == currentBailOutRecord->actualCount);
  1158. Js::CallInfo callInfo(Js::CallFlags_Value, (Js::ArgSlot)inlineeCallInfo.Count);
  1159. Js::ScriptFunction ** functionRef = (Js::ScriptFunction **)&(inlinedFrame->function);
  1160. AnalysisAssert(*functionRef);
  1161. Assert(Js::ScriptFunction::Is(inlinedFrame->function));
  1162. if (*innerMostInlinee == nullptr)
  1163. {
  1164. *innerMostInlinee = *functionRef;
  1165. }
  1166. Js::ArgumentReader args(&callInfo, inlinedFrame->GetArguments());
  1167. Js::Var* pArgumentsObject = &inlinedFrame->arguments;
  1168. (*functionRef)->GetFunctionBody()->EnsureDynamicProfileInfo();
  1169. bailOutReturnValue->returnValue = BailOutHelper(layout, functionRef, args, true, currentBailOutRecord, bailOutOffset,
  1170. returnAddress, bailOutKind, registerSaves, lastBailOutReturnValue, pArgumentsObject, branchValue);
  1171. // Clear the inlinee frame CallInfo, just like we'd have done in JITted code.
  1172. inlinedFrame->callInfo.Clear();
  1173. bailOutReturnValue->returnValueRegSlot = currentBailOutRecord->globalBailOutRecordTable->returnValueRegSlot;
  1174. lastBailOutReturnValue = bailOutReturnValue;
  1175. currentBailOutRecord = currentBailOutRecord->parent;
  1176. bailOutOffset = currentBailOutRecord->bailOutOffset;
  1177. }
  1178. while (currentBailOutRecord->parent != nullptr);
  1179. }
  1180. uint32
  1181. BailOutRecord::BailOutFromLoopBodyHelper(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
  1182. uint32 bailOutOffset, IR::BailOutKind bailOutKind, Js::Var branchValue, Js::Var *registerSaves, BailOutReturnValue * bailOutReturnValue)
  1183. {
  1184. Assert(bailOutRecord->parent == nullptr);
  1185. Js::JavascriptFunction * function = layout->functionObject;
  1186. Js::FunctionBody * executeFunction = function->GetFunctionBody();
  1187. executeFunction->SetRecentlyBailedOutOfJittedLoopBody(true);
  1188. Js::ScriptContext * functionScriptContext = executeFunction->GetScriptContext();
  1189. // Clear the disable implicit call bit in case we bail from that region
  1190. functionScriptContext->GetThreadContext()->ClearDisableImplicitFlags();
  1191. // The current interpreter frame for the loop body
  1192. Js::InterpreterStackFrame * interpreterFrame = functionScriptContext->GetThreadContext()->GetLeafInterpreterFrame();
  1193. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  1194. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1195. #endif
  1196. BAILOUT_KIND_TRACE(executeFunction, bailOutKind, _u("BailOut: function: %s (%s) Loop: %d offset: #%04x Opcode: %s"),
  1197. executeFunction->GetDisplayName(), executeFunction->GetDebugNumberSet(debugStringBuffer), interpreterFrame->GetCurrentLoopNum(),
  1198. bailOutOffset, Js::OpCodeUtil::GetOpCodeName(bailOutRecord->bailOutOpcode));
  1199. BAILOUT_TESTTRACE(executeFunction, bailOutKind, _u("BailOut: function: %s (%s) Loop: %d Opcode: %s\n"), executeFunction->GetDisplayName(),
  1200. executeFunction->GetDebugNumberSet(debugStringBuffer), interpreterFrame->GetCurrentLoopNum(), Js::OpCodeUtil::GetOpCodeName(bailOutRecord->bailOutOpcode));
  1201. // Restore bailout values
  1202. bailOutRecord->RestoreValues(bailOutKind, layout, interpreterFrame, functionScriptContext, true, registerSaves, bailOutReturnValue, layout->GetArgumentsObjectLocation(), branchValue);
  1203. BAILOUT_FLUSH(executeFunction);
  1204. UpdatePolymorphicFieldAccess(function, bailOutRecord);
  1205. // Return the resume byte code offset from the loop body to restart interpreter execution.
  1206. return bailOutOffset;
  1207. }
  1208. void BailOutRecord::UpdatePolymorphicFieldAccess(Js::JavascriptFunction * function, BailOutRecord const * bailOutRecord)
  1209. {
  1210. Js::FunctionBody * executeFunction = bailOutRecord->type == Shared ? ((SharedBailOutRecord*)bailOutRecord)->functionBody : function->GetFunctionBody();
  1211. Js::DynamicProfileInfo *dynamicProfileInfo = nullptr;
  1212. if (executeFunction->HasDynamicProfileInfo())
  1213. {
  1214. dynamicProfileInfo = executeFunction->GetAnyDynamicProfileInfo();
  1215. Assert(dynamicProfileInfo);
  1216. if (bailOutRecord->polymorphicCacheIndex != (uint)-1)
  1217. {
  1218. dynamicProfileInfo->RecordPolymorphicFieldAccess(executeFunction, bailOutRecord->polymorphicCacheIndex);
  1219. if (IR::IsEquivalentTypeCheckBailOutKind(bailOutRecord->bailOutKind))
  1220. {
  1221. // If we've already got a polymorphic inline cache, and if we've got an equivalent type check
  1222. // bailout here, make sure we don't try any more equivalent obj type spec using that cache.
  1223. Js::PolymorphicInlineCache *polymorphicInlineCache = executeFunction->GetPolymorphicInlineCache(
  1224. bailOutRecord->polymorphicCacheIndex);
  1225. if (polymorphicInlineCache)
  1226. {
  1227. polymorphicInlineCache->SetIgnoreForEquivalentObjTypeSpec(true);
  1228. }
  1229. }
  1230. }
  1231. }
  1232. }
  1233. Js::Var
  1234. BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptFunction ** functionRef, Js::Arguments& args, const bool isInlinee,
  1235. BailOutRecord const * bailOutRecord, uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Var * registerSaves, BailOutReturnValue * bailOutReturnValue, Js::Var* pArgumentsObject,
  1236. Js::Var branchValue, void * argoutRestoreAddress)
  1237. {
  1238. Js::ScriptFunction * function = *functionRef;
  1239. Js::FunctionBody * executeFunction = function->GetFunctionBody();
  1240. Js::ScriptContext * functionScriptContext = executeFunction->GetScriptContext();
  1241. // Whether to enter StartCall while doing RestoreValues. We don't do that when bailout due to ignore exception under debugger.
  1242. bool useStartCall = true;
  1243. // Clear the disable implicit call bit in case we bail from that region
  1244. functionScriptContext->GetThreadContext()->ClearDisableImplicitFlags();
  1245. bool isInDebugMode = executeFunction->IsInDebugMode();
  1246. AssertMsg(!isInDebugMode || Js::Configuration::Global.EnableJitInDebugMode(),
  1247. "In diag mode we can get here (function has to be JIT'ed) only when EnableJitInDiagMode is true!");
  1248. // Adjust bailout offset for debug mode (only scenario when we ignore exception).
  1249. if (isInDebugMode)
  1250. {
  1251. Js::DebugManager* debugManager = functionScriptContext->GetThreadContext()->GetDebugManager();
  1252. DebuggingFlags* debuggingFlags = debugManager->GetDebuggingFlags();
  1253. int byteCodeOffsetAfterEx = debuggingFlags->GetByteCodeOffsetAfterIgnoreException();
  1254. // Note that in case where bailout for ignore exception immediately follows regular bailout after a helper,
  1255. // and ignore exception happens, we would bail out with non-exception kind with exception data recorded.
  1256. // In this case we need to treat the bailout as ignore exception one and continue to next/set stmt.
  1257. // This is fine because we only set byteCodeOffsetAfterEx for helpers (HelperMethodWrapper, when enabled)
  1258. // and ignore exception is needed for all helpers.
  1259. if ((bailOutKind & IR::BailOutIgnoreException) || byteCodeOffsetAfterEx != DebuggingFlags::InvalidByteCodeOffset)
  1260. {
  1261. bool needResetData = true;
  1262. // Note: the func # in debuggingFlags still can be 0 in case actual b/o reason was not BailOutIgnoreException,
  1263. // but BailOutIgnoreException was on the OR'ed values for b/o check.
  1264. bool isSameFunction = debuggingFlags->GetFuncNumberAfterIgnoreException() == DebuggingFlags::InvalidFuncNumber ||
  1265. debuggingFlags->GetFuncNumberAfterIgnoreException() == function->GetFunctionBody()->GetFunctionNumber();
  1266. AssertMsg(isSameFunction, "Bailout due to ignore exception in different function, can't bail out cross functions!");
  1267. if (isSameFunction)
  1268. {
  1269. Assert(!(byteCodeOffsetAfterEx == DebuggingFlags::InvalidByteCodeOffset && debuggingFlags->GetFuncNumberAfterIgnoreException() != DebuggingFlags::InvalidFuncNumber));
  1270. if (byteCodeOffsetAfterEx != DebuggingFlags::InvalidByteCodeOffset)
  1271. {
  1272. // We got an exception in native frame, and need to bail out to interpreter
  1273. if (debugManager->stepController.IsActive())
  1274. {
  1275. // Native frame went away, and there will be interpreter frame on its place.
  1276. // Make sure that frameAddrWhenSet it less than current interpreter frame -- we use it to detect stack depth.
  1277. debugManager->stepController.SetFrameAddr(0);
  1278. }
  1279. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1280. if (bailOutOffset != (uint)byteCodeOffsetAfterEx || !(bailOutKind & IR::BailOutIgnoreException))
  1281. {
  1282. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1283. BAILOUT_KIND_TRACE(executeFunction, bailOutKind, _u("BailOut: changing due to ignore exception: function: %s (%s) offset: #%04x -> #%04x Opcode: %s Treating as: %S"), executeFunction->GetDisplayName(),
  1284. executeFunction->GetDebugNumberSet(debugStringBuffer), bailOutOffset, byteCodeOffsetAfterEx, Js::OpCodeUtil::GetOpCodeName(bailOutRecord->bailOutOpcode), ::GetBailOutKindName(IR::BailOutIgnoreException));
  1285. }
  1286. #endif
  1287. // Set the byte code offset to continue from next user statement.
  1288. bailOutOffset = byteCodeOffsetAfterEx;
  1289. // Reset current call count so that we don't do StartCall for inner calls. See WinBlue 272569.
  1290. // The idea is that next statement can never be set to the inner StartCall (another call as part of an ArgOut),
  1291. // it will be next statement in the function.
  1292. useStartCall = false;
  1293. }
  1294. else
  1295. {
  1296. needResetData = false;
  1297. }
  1298. }
  1299. if (needResetData)
  1300. {
  1301. // Reset/correct the flag as either we processed it or we need to correct wrong flag.
  1302. debuggingFlags->ResetByteCodeOffsetAndFuncAfterIgnoreException();
  1303. }
  1304. }
  1305. }
  1306. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  1307. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1308. #endif
  1309. BAILOUT_KIND_TRACE(executeFunction, bailOutKind, _u("BailOut: function: %s (%s) offset: #%04x Opcode: %s"), executeFunction->GetDisplayName(),
  1310. executeFunction->GetDebugNumberSet(debugStringBuffer), bailOutOffset, Js::OpCodeUtil::GetOpCodeName(bailOutRecord->bailOutOpcode));
  1311. BAILOUT_TESTTRACE(executeFunction, bailOutKind, _u("BailOut: function: %s (%s) Opcode: %s\n"), executeFunction->GetDisplayName(),
  1312. executeFunction->GetDebugNumberSet(debugStringBuffer), Js::OpCodeUtil::GetOpCodeName(bailOutRecord->bailOutOpcode));
  1313. if (isInlinee && args.Info.Count != 0)
  1314. {
  1315. // Box arguments. Inlinee arguments may be allocated on the stack.
  1316. for(uint i = 0; i < args.Info.Count; ++i)
  1317. {
  1318. const Js::Var arg = args.Values[i];
  1319. BAILOUT_VERBOSE_TRACE(executeFunction, bailOutKind, _u("BailOut: Argument #%3u: value: 0x%p"), i, arg);
  1320. const Js::Var boxedArg = Js::JavascriptOperators::BoxStackInstance(arg, functionScriptContext, true);
  1321. if(boxedArg != arg)
  1322. {
  1323. args.Values[i] = boxedArg;
  1324. BAILOUT_VERBOSE_TRACE(executeFunction, bailOutKind, _u(" (Boxed: 0x%p)"), boxedArg);
  1325. }
  1326. BAILOUT_VERBOSE_TRACE(executeFunction, bailOutKind, _u("\n"));
  1327. }
  1328. }
  1329. bool fReleaseAlloc = false;
  1330. Js::InterpreterStackFrame* newInstance = nullptr;
  1331. Js::Var* allocation = nullptr;
  1332. if (executeFunction->IsCoroutine())
  1333. {
  1334. // If the FunctionBody is a generator then this call is being made by one of the three
  1335. // generator resuming methods: next(), throw(), or return(). They all pass the generator
  1336. // object as the first of two arguments. The real user arguments are obtained from the
  1337. // generator object. The second argument is the ResumeYieldData which is only needed
  1338. // when resuming a generator and not needed when yielding from a generator, as is occurring
  1339. // here.
  1340. AssertMsg(args.Info.Count == 2, "Generator ScriptFunctions should only be invoked by generator APIs with the pair of arguments they pass in -- the generator object and a ResumeYieldData pointer");
  1341. Js::JavascriptGenerator* generator = Js::JavascriptGenerator::FromVar(args[0]);
  1342. newInstance = generator->GetFrame();
  1343. if (newInstance != nullptr)
  1344. {
  1345. // BailOut will recompute OutArg pointers based on BailOutRecord. Reset them back
  1346. // to initial position before that happens so that OP_StartCall calls don't accumulate
  1347. // incorrectly over multiple yield bailouts.
  1348. newInstance->ResetOut();
  1349. // The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
  1350. // give the InterpreterStackFrame a legit enough stack address to make this comparison work.
  1351. newInstance->m_stackAddress = reinterpret_cast<DWORD_PTR>(&generator);
  1352. }
  1353. else
  1354. {
  1355. //
  1356. // Allocate a new InterpreterStackFrame instance on the recycler heap.
  1357. // It will live with the JavascriptGenerator object.
  1358. //
  1359. Js::Arguments generatorArgs = generator->GetArguments();
  1360. Js::InterpreterStackFrame::Setup setup(function, generatorArgs, true, isInlinee);
  1361. size_t varAllocCount = setup.GetAllocationVarCount();
  1362. size_t varSizeInBytes = varAllocCount * sizeof(Js::Var);
  1363. DWORD_PTR stackAddr = reinterpret_cast<DWORD_PTR>(&generator); // as mentioned above, use any stack address from this frame to ensure correct debugging functionality
  1364. Js::Var loopHeaderArray = executeFunction->GetHasAllocatedLoopHeaders() ? executeFunction->GetLoopHeaderArrayPtr() : nullptr;
  1365. allocation = RecyclerNewPlus(functionScriptContext->GetRecycler(), varSizeInBytes, Js::Var);
  1366. // Initialize the interpreter stack frame (constants) but not the param, the bailout record will restore the value
  1367. #if DBG
  1368. // Allocate invalidVar on GC instead of stack since this InterpreterStackFrame will out live the current real frame
  1369. Js::Var invalidVar = (Js::RecyclableObject*)RecyclerNewPlusLeaf(functionScriptContext->GetRecycler(), sizeof(Js::RecyclableObject), Js::Var);
  1370. memset(invalidVar, 0xFE, sizeof(Js::RecyclableObject));
  1371. newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, stackAddr, invalidVar);
  1372. #else
  1373. newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, stackAddr);
  1374. #endif
  1375. newInstance->m_reader.Create(executeFunction);
  1376. generator->SetFrame(newInstance, varSizeInBytes);
  1377. }
  1378. }
  1379. else
  1380. {
  1381. Js::InterpreterStackFrame::Setup setup(function, args, true, isInlinee);
  1382. size_t varAllocCount = setup.GetAllocationVarCount();
  1383. size_t varSizeInBytes = varAllocCount * sizeof(Js::Var);
  1384. // If the locals area exceeds a certain limit, allocate it from a private arena rather than
  1385. // this frame. The current limit is based on an old assert on the number of locals we would allow here.
  1386. if (varAllocCount > Js::InterpreterStackFrame::LocalsThreshold)
  1387. {
  1388. ArenaAllocator *tmpAlloc = nullptr;
  1389. fReleaseAlloc = functionScriptContext->EnsureInterpreterArena(&tmpAlloc);
  1390. allocation = (Js::Var*)tmpAlloc->Alloc(varSizeInBytes);
  1391. }
  1392. else
  1393. {
  1394. PROBE_STACK_PARTIAL_INITIALIZED_BAILOUT_FRAME(functionScriptContext, Js::Constants::MinStackInterpreter + varSizeInBytes, returnAddress);
  1395. allocation = (Js::Var*)_alloca(varSizeInBytes);
  1396. }
  1397. Js::Var loopHeaderArray = nullptr;
  1398. if (executeFunction->GetHasAllocatedLoopHeaders())
  1399. {
  1400. // Loop header array is recycler allocated, so we push it on the stack
  1401. // When we scan the stack, we'll recognize it as a recycler allocated
  1402. // object, and mark it's contents and keep the individual loop header
  1403. // wrappers alive
  1404. loopHeaderArray = executeFunction->GetLoopHeaderArrayPtr();
  1405. }
  1406. // Set stack address for STEP_OUT/recursion detection for new frame.
  1407. // This frame is originally jitted frame for which we create a new interpreter frame on top of it on stack,
  1408. // set the stack address to some stack location that belong to the original jitted frame.
  1409. DWORD_PTR frameStackAddr = reinterpret_cast<DWORD_PTR>(layout->GetArgv());
  1410. // Initialize the interpreter stack frame (constants) but not the param, the bailout record will restore the value
  1411. #if DBG
  1412. Js::Var invalidStackVar = (Js::RecyclableObject*)_alloca(sizeof(Js::RecyclableObject));
  1413. memset(invalidStackVar, 0xFE, sizeof(Js::RecyclableObject));
  1414. newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, frameStackAddr, invalidStackVar);
  1415. #else
  1416. newInstance = setup.InitializeAllocation(allocation, false, false, loopHeaderArray, frameStackAddr);
  1417. #endif
  1418. newInstance->m_reader.Create(executeFunction);
  1419. }
  1420. int forInEnumeratorArrayRestoreOffset = bailOutRecord->globalBailOutRecordTable->forInEnumeratorArrayRestoreOffset;
  1421. if (forInEnumeratorArrayRestoreOffset != -1)
  1422. {
  1423. newInstance->forInObjectEnumerators = layout->GetForInObjectEnumeratorArrayAtOffset(forInEnumeratorArrayRestoreOffset);
  1424. }
  1425. newInstance->ehBailoutData = bailOutRecord->ehBailoutData;
  1426. newInstance->OrFlags(Js::InterpreterStackFrameFlags_FromBailOut);
  1427. if (bailOutKind == IR::BailOutOnArrayAccessHelperCall)
  1428. {
  1429. newInstance->OrFlags(Js::InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall);
  1430. }
  1431. ThreadContext *threadContext = newInstance->GetScriptContext()->GetThreadContext();
  1432. // If this is a bailout on implicit calls, then it must have occurred at the current statement.
  1433. // Otherwise, assume that the bits are stale, so clear them before entering the interpreter.
  1434. if (!BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
  1435. {
  1436. threadContext->ClearImplicitCallFlags();
  1437. }
  1438. Js::RegSlot varCount = function->GetFunctionBody()->GetVarCount();
  1439. if (varCount)
  1440. {
  1441. Js::RegSlot constantCount = function->GetFunctionBody()->GetConstantCount();
  1442. memset(newInstance->m_localSlots + constantCount, 0, varCount * sizeof(Js::Var));
  1443. }
  1444. Js::RegSlot localFrameDisplayReg = executeFunction->GetLocalFrameDisplayRegister();
  1445. Js::RegSlot localClosureReg = executeFunction->GetLocalClosureRegister();
  1446. Js::RegSlot paramClosureReg = executeFunction->GetParamClosureRegister();
  1447. if (!isInlinee)
  1448. {
  1449. // If byte code was generated to do stack closures, restore closure pointers before the normal RestoreValues.
  1450. // If code was jitted for stack closures, we have to restore the pointers from known stack locations.
  1451. // (RestoreValues won't do it.) If stack closures were disabled for this function before we jitted,
  1452. // then the values on the stack are garbage, but if we need them then RestoreValues will overwrite with
  1453. // the correct values.
  1454. if (localFrameDisplayReg != Js::Constants::NoRegister)
  1455. {
  1456. Js::FrameDisplay *localFrameDisplay;
  1457. uintptr_t frameDisplayIndex = (uintptr_t)(
  1458. #if _M_IX86 || _M_AMD64
  1459. executeFunction->GetInParamsCount() == 0 ?
  1460. Js::JavascriptFunctionArgIndex_StackFrameDisplayNoArg :
  1461. #endif
  1462. Js::JavascriptFunctionArgIndex_StackFrameDisplay) - 2;
  1463. localFrameDisplay = (Js::FrameDisplay*)layout->GetArgv()[frameDisplayIndex];
  1464. newInstance->SetLocalFrameDisplay(localFrameDisplay);
  1465. }
  1466. if (localClosureReg != Js::Constants::NoRegister)
  1467. {
  1468. Js::Var localClosure;
  1469. uintptr_t scopeSlotsIndex = (uintptr_t)(
  1470. #if _M_IX86 || _M_AMD64
  1471. executeFunction->GetInParamsCount() == 0 ?
  1472. Js::JavascriptFunctionArgIndex_StackScopeSlotsNoArg :
  1473. #endif
  1474. Js::JavascriptFunctionArgIndex_StackScopeSlots) - 2;
  1475. localClosure = layout->GetArgv()[scopeSlotsIndex];
  1476. newInstance->SetLocalClosure(localClosure);
  1477. }
  1478. }
  1479. // Restore bailout values
  1480. bailOutRecord->RestoreValues(bailOutKind, layout, newInstance, functionScriptContext, false, registerSaves, bailOutReturnValue, pArgumentsObject, branchValue, returnAddress, useStartCall, argoutRestoreAddress);
  1481. // For functions that don't get the scope slot and frame display pointers back from the known stack locations
  1482. // (see above), get them back from the designated registers.
  1483. // In either case, clear the values from those registers, because the interpreter should not be able to access
  1484. // those values through the registers (only through its private fields).
  1485. if (localFrameDisplayReg != Js::Constants::NoRegister)
  1486. {
  1487. Js::FrameDisplay *frameDisplay = (Js::FrameDisplay*)newInstance->GetNonVarReg(localFrameDisplayReg);
  1488. if (frameDisplay)
  1489. {
  1490. newInstance->SetLocalFrameDisplay(frameDisplay);
  1491. newInstance->SetNonVarReg(localFrameDisplayReg, nullptr);
  1492. }
  1493. }
  1494. if (localClosureReg != Js::Constants::NoRegister)
  1495. {
  1496. Js::Var closure = newInstance->GetNonVarReg(localClosureReg);
  1497. if (closure)
  1498. {
  1499. bailOutRecord->globalBailOutRecordTable->isScopeObjRestored = true;
  1500. newInstance->SetLocalClosure(closure);
  1501. newInstance->SetNonVarReg(localClosureReg, nullptr);
  1502. }
  1503. }
  1504. if (paramClosureReg != Js::Constants::NoRegister)
  1505. {
  1506. Js::Var closure = newInstance->GetNonVarReg(paramClosureReg);
  1507. if (closure)
  1508. {
  1509. newInstance->SetParamClosure(closure);
  1510. newInstance->SetNonVarReg(paramClosureReg, nullptr);
  1511. }
  1512. }
  1513. if (bailOutRecord->globalBailOutRecordTable->hasStackArgOpt)
  1514. {
  1515. newInstance->TrySetFrameObjectInHeapArgObj(functionScriptContext, bailOutRecord->globalBailOutRecordTable->hasNonSimpleParams,
  1516. bailOutRecord->globalBailOutRecordTable->isScopeObjRestored);
  1517. }
  1518. //Reset the value for tracking the restoration during next bail out.
  1519. bailOutRecord->globalBailOutRecordTable->isScopeObjRestored = false;
  1520. uint32 innerScopeCount = executeFunction->GetInnerScopeCount();
  1521. for (uint32 i = 0; i < innerScopeCount; i++)
  1522. {
  1523. Js::RegSlot reg = executeFunction->GetFirstInnerScopeRegister() + i;
  1524. newInstance->SetInnerScopeFromIndex(i, newInstance->GetNonVarReg(reg));
  1525. newInstance->SetNonVarReg(reg, nullptr);
  1526. }
  1527. newInstance->SetClosureInitDone(bailOutOffset != 0 || !(bailOutKind & IR::BailOutForDebuggerBits));
  1528. // RestoreValues may call EnsureArguments and cause functions to be boxed.
  1529. // Since the interpreter frame that hasn't started yet, StackScriptFunction::Box would not have replaced the function object
  1530. // in the restoring interpreter frame. Let's make sure the current interpreter frame has the unboxed version.
  1531. // Note: Only use the unboxed version if we have replaced the function argument on the stack via boxing
  1532. // so that the interpreter frame we are bailing out to matches the one in the function argument list
  1533. // (which is used by the stack walker to match up stack frame and the interpreter frame).
  1534. // Some function are boxed but we continue to use the stack version to call the function - those that only live in register
  1535. // and are not captured in frame displays.
  1536. // Those uses are fine, but that means the function argument list will have the stack function object that is passed it and
  1537. // not be replaced with a just boxed one.
  1538. Js::ScriptFunction * currentFunctionObject = *functionRef;
  1539. if (function != currentFunctionObject)
  1540. {
  1541. Assert(currentFunctionObject == Js::StackScriptFunction::GetCurrentFunctionObject(function));
  1542. newInstance->SetExecutingStackFunction(currentFunctionObject);
  1543. }
  1544. UpdatePolymorphicFieldAccess(function, bailOutRecord);
  1545. BAILOUT_FLUSH(executeFunction);
  1546. executeFunction->BeginExecution();
  1547. // Restart at the bailout byte code offset.
  1548. newInstance->m_reader.SetCurrentOffset(bailOutOffset);
  1549. Js::Var aReturn = nullptr;
  1550. {
  1551. // Following _AddressOfReturnAddress <= real address of "returnAddress". Suffices for RemoteStackWalker to test partially initialized interpreter frame.
  1552. Js::InterpreterStackFrame::PushPopFrameHelper pushPopFrameHelper(newInstance, returnAddress, _AddressOfReturnAddress());
  1553. aReturn = isInDebugMode ? newInstance->DebugProcess() : newInstance->Process();
  1554. // Note: in debug mode we always have to bailout to debug thunk,
  1555. // as normal interpreter thunk expects byte code compiled w/o debugging.
  1556. }
  1557. executeFunction->EndExecution();
  1558. if (executeFunction->HasDynamicProfileInfo())
  1559. {
  1560. Js::DynamicProfileInfo *dynamicProfileInfo = executeFunction->GetAnyDynamicProfileInfo();
  1561. dynamicProfileInfo->RecordImplicitCallFlags(threadContext->GetImplicitCallFlags());
  1562. }
  1563. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("BailOut: Return Value: 0x%p"), aReturn);
  1564. if (bailOutRecord->globalBailOutRecordTable->isInlinedConstructor)
  1565. {
  1566. AssertMsg(!executeFunction->IsGenerator(), "Generator functions are not expected to be inlined. If this changes then need to use the real user args here from the generator object");
  1567. Assert(args.Info.Count != 0);
  1568. aReturn = Js::JavascriptFunction::FinishConstructor(aReturn, args.Values[0], function);
  1569. Js::Var oldValue = aReturn;
  1570. aReturn = Js::JavascriptOperators::BoxStackInstance(oldValue, functionScriptContext, /* allowStackFunction */ true);
  1571. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1572. if (oldValue != aReturn)
  1573. {
  1574. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(" (Boxed: 0x%p)"), aReturn);
  1575. }
  1576. #endif
  1577. }
  1578. BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("\n"));
  1579. return aReturn;
  1580. }
  1581. // Note on rejit states
  1582. //
  1583. // To avoid always incurring the cost of collecting runtime stats (function calls count and valid bailOutKind),
  1584. // the initial codegen'd version of a function does not collect them. After a second bailout we rejit the function
  1585. // with runtime stats collection. On subsequent bailouts we can evaluate our heuristics and decide whether to rejit.
  1586. //
  1587. // Function bodies always use the least optimized version of the code as default. At the same time, there can be
  1588. // function objects with some older, more optimized, version of the code active. When a bailout occurs out of such
  1589. // code we avoid a rejit by checking if the offending optimization has been disabled in the default code and if so
  1590. // we "rethunk" the bailing out function rather that incurring a rejit.
  1591. // actualBailOutOffset - bail out offset in the function, inlinee or otherwise, that had the bailout.
  1592. void BailOutRecord::ScheduleFunctionCodeGen(Js::ScriptFunction * function, Js::ScriptFunction * innerMostInlinee,
  1593. BailOutRecord const * bailOutRecord, IR::BailOutKind bailOutKind, uint32 actualBailOutOffset, Js::ImplicitCallFlags savedImplicitCallFlags, void * returnAddress)
  1594. {
  1595. if (bailOutKind == IR::BailOnSimpleJitToFullJitLoopBody ||
  1596. bailOutKind == IR::BailOutForGeneratorYield ||
  1597. bailOutKind == IR::LazyBailOut)
  1598. {
  1599. return;
  1600. }
  1601. Js::FunctionBody * executeFunction = function->GetFunctionBody();
  1602. if (PHASE_OFF(Js::ReJITPhase, executeFunction))
  1603. {
  1604. return;
  1605. }
  1606. Js::AutoPushReturnAddressForStackWalker saveReturnAddress(executeFunction->GetScriptContext(), returnAddress);
  1607. BailOutRecord * bailOutRecordNotConst = (BailOutRecord *)(void *)bailOutRecord;
  1608. bailOutRecordNotConst->bailOutCount++;
  1609. Js::FunctionEntryPointInfo *entryPointInfo = function->GetFunctionEntryPointInfo();
  1610. uint8 callsCount = entryPointInfo->callsCount > 255 ? 255 : static_cast<uint8>(entryPointInfo->callsCount);
  1611. RejitReason rejitReason = RejitReason::None;
  1612. bool reThunk = false;
  1613. callsCount = callsCount <= Js::FunctionEntryPointInfo::GetDecrCallCountPerBailout() ? 0 : callsCount - Js::FunctionEntryPointInfo::GetDecrCallCountPerBailout() ;
  1614. CheckPreemptiveRejit(executeFunction, bailOutKind, bailOutRecordNotConst, callsCount, -1);
  1615. entryPointInfo->callsCount = callsCount;
  1616. Assert(bailOutKind != IR::BailOutInvalid);
  1617. if ((executeFunction->HasDynamicProfileInfo() && callsCount == 0) ||
  1618. PHASE_FORCE(Js::ReJITPhase, executeFunction))
  1619. {
  1620. Js::DynamicProfileInfo * profileInfo = executeFunction->GetAnyDynamicProfileInfo();
  1621. if ((bailOutKind & (IR::BailOutOnResultConditions | IR::BailOutOnDivSrcConditions)) || bailOutKind == IR::BailOutIntOnly || bailOutKind == IR::BailOnIntMin || bailOutKind == IR::BailOnDivResultNotInt)
  1622. {
  1623. // Note WRT BailOnIntMin: it wouldn't make sense to re-jit without changing anything here, as interpreter will not change the (int) type,
  1624. // so the options are: (1) rejit with disabling int type spec, (2) don't rejit, always bailout.
  1625. // It seems to be better to rejit.
  1626. if (bailOutKind & IR::BailOutOnMulOverflow)
  1627. {
  1628. if (profileInfo->IsAggressiveMulIntTypeSpecDisabled(false))
  1629. {
  1630. reThunk = true;
  1631. }
  1632. else
  1633. {
  1634. profileInfo->DisableAggressiveMulIntTypeSpec(false);
  1635. rejitReason = RejitReason::AggressiveMulIntTypeSpecDisabled;
  1636. }
  1637. }
  1638. else if ((bailOutKind & (IR::BailOutOnDivByZero | IR::BailOutOnDivOfMinInt)) || bailOutKind == IR::BailOnDivResultNotInt)
  1639. {
  1640. if (profileInfo->IsDivIntTypeSpecDisabled(false))
  1641. {
  1642. reThunk = true;
  1643. }
  1644. else
  1645. {
  1646. profileInfo->DisableDivIntTypeSpec(false);
  1647. rejitReason = RejitReason::DivIntTypeSpecDisabled;
  1648. }
  1649. }
  1650. else
  1651. {
  1652. if (profileInfo->IsAggressiveIntTypeSpecDisabled(false))
  1653. {
  1654. reThunk = true;
  1655. }
  1656. else
  1657. {
  1658. profileInfo->DisableAggressiveIntTypeSpec(false);
  1659. rejitReason = RejitReason::AggressiveIntTypeSpecDisabled;
  1660. }
  1661. }
  1662. }
  1663. else if (bailOutKind & IR::BailOutForDebuggerBits)
  1664. {
  1665. // Do not rejit, do not rethunk, just ignore the bailout.
  1666. }
  1667. else switch(bailOutKind)
  1668. {
  1669. case IR::BailOutOnNotPrimitive:
  1670. if (profileInfo->IsLossyIntTypeSpecDisabled())
  1671. {
  1672. reThunk = true;
  1673. }
  1674. else
  1675. {
  1676. profileInfo->DisableLossyIntTypeSpec();
  1677. rejitReason = RejitReason::LossyIntTypeSpecDisabled;
  1678. }
  1679. break;
  1680. case IR::BailOutOnMemOpError:
  1681. if (profileInfo->IsMemOpDisabled())
  1682. {
  1683. reThunk = true;
  1684. }
  1685. else
  1686. {
  1687. profileInfo->DisableMemOp();
  1688. rejitReason = RejitReason::MemOpDisabled;
  1689. }
  1690. break;
  1691. case IR::BailOutPrimitiveButString:
  1692. case IR::BailOutNumberOnly:
  1693. if (profileInfo->IsFloatTypeSpecDisabled())
  1694. {
  1695. reThunk = true;
  1696. }
  1697. else
  1698. {
  1699. profileInfo->DisableFloatTypeSpec();
  1700. rejitReason = RejitReason::FloatTypeSpecDisabled;
  1701. }
  1702. break;
  1703. case IR::BailOutOnImplicitCalls:
  1704. case IR::BailOutOnImplicitCallsPreOp:
  1705. // Check if the implicit call flags in the profile have changed since we last JITed this
  1706. // function body. If so, and they indicate an implicit call of some sort occurred
  1707. // then we need to reJIT.
  1708. if ((executeFunction->GetSavedImplicitCallsFlags() & savedImplicitCallFlags) == Js::ImplicitCall_None)
  1709. {
  1710. profileInfo->RecordImplicitCallFlags(savedImplicitCallFlags);
  1711. profileInfo->DisableLoopImplicitCallInfo();
  1712. rejitReason = RejitReason::ImplicitCallFlagsChanged;
  1713. }
  1714. else
  1715. {
  1716. reThunk = true;
  1717. }
  1718. break;
  1719. case IR::BailOnModByPowerOf2:
  1720. rejitReason = RejitReason::ModByPowerOf2;
  1721. break;
  1722. case IR::BailOutOnNotArray:
  1723. if(profileInfo->IsArrayCheckHoistDisabled(false))
  1724. {
  1725. reThunk = true;
  1726. }
  1727. else
  1728. {
  1729. profileInfo->DisableArrayCheckHoist(false);
  1730. rejitReason = RejitReason::ArrayCheckHoistDisabled;
  1731. }
  1732. break;
  1733. case IR::BailOutOnNotNativeArray:
  1734. // REVIEW: We have an issue with array profile info. The info on the type of array we have won't
  1735. // get fixed by rejitting. For now, just give up after 50 rejits.
  1736. if (profileInfo->GetRejitCount() >= 50)
  1737. {
  1738. reThunk = true;
  1739. }
  1740. else
  1741. {
  1742. rejitReason = RejitReason::ExpectingNativeArray;
  1743. }
  1744. break;
  1745. case IR::BailOutConvertedNativeArray:
  1746. rejitReason = RejitReason::ConvertedNativeArray;
  1747. break;
  1748. case IR::BailOutConventionalTypedArrayAccessOnly:
  1749. if(profileInfo->IsTypedArrayTypeSpecDisabled(false))
  1750. {
  1751. reThunk = true;
  1752. }
  1753. else
  1754. {
  1755. profileInfo->DisableTypedArrayTypeSpec(false);
  1756. rejitReason = RejitReason::TypedArrayTypeSpecDisabled;
  1757. }
  1758. break;
  1759. case IR::BailOutConventionalNativeArrayAccessOnly:
  1760. rejitReason = RejitReason::ExpectingConventionalNativeArrayAccess;
  1761. break;
  1762. case IR::BailOutOnMissingValue:
  1763. if(profileInfo->IsArrayMissingValueCheckHoistDisabled(false))
  1764. {
  1765. reThunk = true;
  1766. }
  1767. else
  1768. {
  1769. profileInfo->DisableArrayMissingValueCheckHoist(false);
  1770. rejitReason = RejitReason::ArrayMissingValueCheckHoistDisabled;
  1771. }
  1772. break;
  1773. case IR::BailOutOnArrayAccessHelperCall:
  1774. // This is a pre-op bailout, so the interpreter will update the profile data for this byte-code instruction to
  1775. // prevent excessive bailouts here in the future
  1776. rejitReason = RejitReason::ArrayAccessNeededHelperCall;
  1777. break;
  1778. case IR::BailOutOnInvalidatedArrayHeadSegment:
  1779. if(profileInfo->IsJsArraySegmentHoistDisabled(false))
  1780. {
  1781. reThunk = true;
  1782. }
  1783. else
  1784. {
  1785. profileInfo->DisableJsArraySegmentHoist(false);
  1786. rejitReason = RejitReason::JsArraySegmentHoistDisabled;
  1787. }
  1788. break;
  1789. case IR::BailOutOnIrregularLength:
  1790. if(profileInfo->IsLdLenIntSpecDisabled())
  1791. {
  1792. reThunk = true;
  1793. }
  1794. else
  1795. {
  1796. profileInfo->DisableLdLenIntSpec();
  1797. rejitReason = RejitReason::LdLenIntSpecDisabled;
  1798. }
  1799. break;
  1800. case IR::BailOutOnFailedHoistedBoundCheck:
  1801. if(profileInfo->IsBoundCheckHoistDisabled(false))
  1802. {
  1803. reThunk = true;
  1804. }
  1805. else
  1806. {
  1807. profileInfo->DisableBoundCheckHoist(false);
  1808. rejitReason = RejitReason::BoundCheckHoistDisabled;
  1809. }
  1810. break;
  1811. case IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck:
  1812. if(profileInfo->IsLoopCountBasedBoundCheckHoistDisabled(false))
  1813. {
  1814. reThunk = true;
  1815. }
  1816. else
  1817. {
  1818. profileInfo->DisableLoopCountBasedBoundCheckHoist(false);
  1819. rejitReason = RejitReason::LoopCountBasedBoundCheckHoistDisabled;
  1820. }
  1821. break;
  1822. case IR::BailOutExpectingInteger:
  1823. if (profileInfo->IsSwitchOptDisabled())
  1824. {
  1825. reThunk = true;
  1826. }
  1827. else
  1828. {
  1829. profileInfo->DisableSwitchOpt();
  1830. rejitReason = RejitReason::DisableSwitchOptExpectingInteger;
  1831. }
  1832. break;
  1833. case IR::BailOutExpectingString:
  1834. if (profileInfo->IsSwitchOptDisabled())
  1835. {
  1836. reThunk = true;
  1837. }
  1838. else
  1839. {
  1840. profileInfo->DisableSwitchOpt();
  1841. rejitReason = RejitReason::DisableSwitchOptExpectingString;
  1842. }
  1843. break;
  1844. case IR::BailOutOnFailedPolymorphicInlineTypeCheck:
  1845. rejitReason = RejitReason::FailedPolymorphicInlineeTypeCheck;
  1846. break;
  1847. case IR::BailOnStackArgsOutOfActualsRange:
  1848. if (profileInfo->IsStackArgOptDisabled())
  1849. {
  1850. reThunk = true;
  1851. }
  1852. else
  1853. {
  1854. profileInfo->DisableStackArgOpt();
  1855. rejitReason = RejitReason::DisableStackArgOpt;
  1856. }
  1857. break;
  1858. case IR::BailOutOnPolymorphicInlineFunction:
  1859. case IR::BailOutFailedInlineTypeCheck:
  1860. case IR::BailOutOnInlineFunction:
  1861. // Check if the inliner state has changed since we last JITed this function body. If so
  1862. // then we need to reJIT.
  1863. if (innerMostInlinee)
  1864. {
  1865. // There is no way now to check if the inlinee version has changed. Just rejit.
  1866. // This should be changed to getting the inliner version corresponding to inlinee.
  1867. rejitReason = RejitReason::InlineeChanged;
  1868. }
  1869. else
  1870. {
  1871. if (executeFunction->GetSavedInlinerVersion() == profileInfo->GetInlinerVersion())
  1872. {
  1873. reThunk = true;
  1874. }
  1875. else
  1876. {
  1877. rejitReason = RejitReason::InlineeChanged;
  1878. }
  1879. }
  1880. break;
  1881. case IR::BailOutOnNoProfile:
  1882. if (profileInfo->IsNoProfileBailoutsDisabled())
  1883. {
  1884. reThunk = true;
  1885. }
  1886. else if (executeFunction->IncrementBailOnMisingProfileRejitCount() > (uint)CONFIG_FLAG(BailOnNoProfileRejitLimit))
  1887. {
  1888. profileInfo->DisableNoProfileBailouts();
  1889. rejitReason = RejitReason::NoProfile;
  1890. }
  1891. else
  1892. {
  1893. executeFunction->ResetBailOnMisingProfileCount();
  1894. rejitReason = RejitReason::NoProfile;
  1895. }
  1896. break;
  1897. case IR::BailOutCheckThis:
  1898. // Presumably we've started passing a different "this" pointer to callees.
  1899. if (profileInfo->IsCheckThisDisabled())
  1900. {
  1901. reThunk = true;
  1902. }
  1903. else
  1904. {
  1905. profileInfo->DisableCheckThis();
  1906. rejitReason = RejitReason::CheckThisDisabled;
  1907. }
  1908. break;
  1909. case IR::BailOutOnTaggedValue:
  1910. if (profileInfo->IsTagCheckDisabled())
  1911. {
  1912. reThunk = true;
  1913. }
  1914. else
  1915. {
  1916. profileInfo->DisableTagCheck();
  1917. rejitReason = RejitReason::FailedTagCheck;
  1918. }
  1919. break;
  1920. case IR::BailOutFailedTypeCheck:
  1921. case IR::BailOutFailedFixedFieldTypeCheck:
  1922. {
  1923. // An inline cache must have gone from monomorphic to polymorphic.
  1924. // This is already noted in the profile data, so optimization of the given ld/st will
  1925. // be inhibited on re-jit.
  1926. // Consider disabling the optimization across the function after n failed type checks.
  1927. if (innerMostInlinee)
  1928. {
  1929. rejitReason = bailOutKind == IR::BailOutFailedTypeCheck ? RejitReason::FailedTypeCheck : RejitReason::FailedFixedFieldTypeCheck;
  1930. }
  1931. else
  1932. {
  1933. uint32 state;
  1934. state = profileInfo->GetPolymorphicCacheState();
  1935. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1936. if (PHASE_TRACE(Js::ObjTypeSpecPhase, executeFunction))
  1937. {
  1938. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1939. Output::Print(
  1940. _u("Objtypespec (%s): States on bailout: Saved cache: %d, Live cache: %d\n"),
  1941. executeFunction->GetDebugNumberSet(debugStringBuffer), executeFunction->GetSavedPolymorphicCacheState(), state);
  1942. Output::Flush();
  1943. }
  1944. #endif
  1945. if (state <= executeFunction->GetSavedPolymorphicCacheState())
  1946. {
  1947. reThunk = true;
  1948. }
  1949. else
  1950. {
  1951. rejitReason = bailOutKind == IR::BailOutFailedTypeCheck ?
  1952. RejitReason::FailedTypeCheck : RejitReason::FailedFixedFieldTypeCheck;
  1953. }
  1954. }
  1955. break;
  1956. }
  1957. case IR::BailOutFailedEquivalentTypeCheck:
  1958. case IR::BailOutFailedEquivalentFixedFieldTypeCheck:
  1959. if (profileInfo->IsEquivalentObjTypeSpecDisabled())
  1960. {
  1961. reThunk = true;
  1962. }
  1963. else
  1964. {
  1965. rejitReason = bailOutKind == IR::BailOutFailedEquivalentTypeCheck ?
  1966. RejitReason::FailedEquivalentTypeCheck : RejitReason::FailedEquivalentFixedFieldTypeCheck;
  1967. }
  1968. break;
  1969. case IR::BailOutFailedFixedFieldCheck:
  1970. rejitReason = RejitReason::FailedFixedFieldCheck;
  1971. break;
  1972. case IR::BailOutFailedCtorGuardCheck:
  1973. // (ObjTypeSpec): Consider scheduling re-JIT right after the first bailout. We will never successfully execute the
  1974. // function from which we just bailed out, unless we take a different code path through it.
  1975. // A constructor cache guard may be invalidated for one of two reasons:
  1976. // a) the constructor's prototype property has changed, or
  1977. // b) one of the properties protected by the guard (this constructor cache served as) has changed in some way (e.g. became read-only).
  1978. // In the former case, the cache itself will be marked as polymorphic and on re-JIT we won't do the optimization.
  1979. // In the latter case, the inline cache for the offending property will be cleared and on re-JIT the guard will not be enlisted
  1980. // to protect that property operation.
  1981. rejitReason = RejitReason::CtorGuardInvalidated;
  1982. break;
  1983. case IR::BailOutOnFloor:
  1984. {
  1985. if (profileInfo->IsFloorInliningDisabled())
  1986. {
  1987. reThunk = true;
  1988. }
  1989. else
  1990. {
  1991. profileInfo->DisableFloorInlining();
  1992. rejitReason = RejitReason::FloorInliningDisabled;
  1993. }
  1994. break;
  1995. }
  1996. case IR::BailOutOnPowIntIntOverflow:
  1997. {
  1998. if (profileInfo->IsPowIntIntTypeSpecDisabled())
  1999. {
  2000. reThunk = true;
  2001. }
  2002. else
  2003. {
  2004. profileInfo->DisablePowIntIntTypeSpec();
  2005. rejitReason = RejitReason::PowIntIntTypeSpecDisabled;
  2006. }
  2007. break;
  2008. }
  2009. case IR::BailOutOnEarlyExit:
  2010. {
  2011. if (profileInfo->IsOptimizeTryFinallyDisabled())
  2012. {
  2013. reThunk = true;
  2014. }
  2015. else
  2016. {
  2017. profileInfo->DisableOptimizeTryFinally();
  2018. rejitReason = RejitReason::OptimizeTryFinallyDisabled;
  2019. }
  2020. break;
  2021. }
  2022. }
  2023. Assert(!(rejitReason != RejitReason::None && reThunk));
  2024. }
  2025. if(PHASE_FORCE(Js::ReJITPhase, executeFunction) && rejitReason == RejitReason::None)
  2026. {
  2027. rejitReason = RejitReason::Forced;
  2028. }
  2029. if (!reThunk && rejitReason != RejitReason::None)
  2030. {
  2031. Js::DynamicProfileInfo * profileInfo = executeFunction->GetAnyDynamicProfileInfo();
  2032. // REVIEW: Temporary fix for RS1. Disable Rejiting if it looks like it is not fixing the problem.
  2033. // For RS2, turn the rejitCount check into an assert and let's fix all these issues.
  2034. if (profileInfo->GetRejitCount() >= 100 ||
  2035. (profileInfo->GetBailOutOffsetForLastRejit() == actualBailOutOffset && function->IsNewEntryPointAvailable()))
  2036. {
  2037. #ifdef REJIT_STATS
  2038. Js::ScriptContext* scriptContext = executeFunction->GetScriptContext();
  2039. if (scriptContext->rejitReasonCountsCap != nullptr)
  2040. {
  2041. scriptContext->rejitReasonCountsCap[static_cast<byte>(rejitReason)]++;
  2042. }
  2043. #endif
  2044. reThunk = true;
  2045. rejitReason = RejitReason::None;
  2046. }
  2047. else
  2048. {
  2049. profileInfo->IncRejitCount();
  2050. profileInfo->SetBailOutOffsetForLastRejit(actualBailOutOffset);
  2051. }
  2052. }
  2053. REJIT_KIND_TESTTRACE(bailOutKind, _u("Bailout from function: function: %s, bailOutKindName: (%S), bailOutCount: %d, callCount: %d, reJitReason: %S, reThunk: %s\r\n"),
  2054. function->GetFunctionBody()->GetDisplayName(), ::GetBailOutKindName(bailOutKind), bailOutRecord->bailOutCount, callsCount,
  2055. GetRejitReasonName(rejitReason), reThunk ? trueString : falseString);
  2056. #ifdef REJIT_STATS
  2057. executeFunction->GetScriptContext()->LogBailout(executeFunction, bailOutKind);
  2058. if (bailOutRecord->bailOutCount > 500)
  2059. {
  2060. Js::ScriptContext* scriptContext = executeFunction->GetScriptContext();
  2061. auto bailoutReasonCountsCap = scriptContext->bailoutReasonCountsCap;
  2062. if (bailoutReasonCountsCap != nullptr)
  2063. {
  2064. if (!bailoutReasonCountsCap->ContainsKey(bailOutKind))
  2065. {
  2066. bailoutReasonCountsCap->Item(bailOutKind, 1);
  2067. }
  2068. else
  2069. {
  2070. uint val = bailoutReasonCountsCap->Item(bailOutKind);
  2071. ++val;
  2072. bailoutReasonCountsCap->Item(bailOutKind, val);
  2073. }
  2074. }
  2075. }
  2076. #endif
  2077. if (reThunk && executeFunction->DontRethunkAfterBailout())
  2078. {
  2079. // This function is marked for rethunking, but the last ReJIT we've done was for a JIT loop body
  2080. // So the latest rejitted version of this function may not have the right optimization disabled.
  2081. // Rejit just to be safe.
  2082. reThunk = false;
  2083. rejitReason = RejitReason::AfterLoopBodyRejit;
  2084. }
  2085. if (reThunk)
  2086. {
  2087. Js::FunctionEntryPointInfo *const defaultEntryPointInfo = executeFunction->GetDefaultFunctionEntryPointInfo();
  2088. function->UpdateThunkEntryPoint(defaultEntryPointInfo, executeFunction->GetDirectEntryPoint(defaultEntryPointInfo));
  2089. }
  2090. else if (rejitReason != RejitReason::None)
  2091. {
  2092. #ifdef REJIT_STATS
  2093. executeFunction->GetScriptContext()->LogRejit(executeFunction, rejitReason);
  2094. #endif
  2095. executeFunction->ClearDontRethunkAfterBailout();
  2096. GenerateFunction(executeFunction->GetScriptContext()->GetNativeCodeGenerator(), executeFunction, function);
  2097. if(executeFunction->GetExecutionMode() != ExecutionMode::FullJit)
  2098. {
  2099. // With expiry, it's possible that the execution mode is currently interpreter or simple JIT. Transition to full JIT
  2100. // after successfully scheduling the rejit work item (in case of OOM).
  2101. executeFunction->TraceExecutionMode("Rejit (before)");
  2102. executeFunction->TransitionToFullJitExecutionMode();
  2103. executeFunction->TraceExecutionMode("Rejit");
  2104. }
  2105. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2106. if(PHASE_TRACE(Js::ReJITPhase, executeFunction))
  2107. {
  2108. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2109. Output::Print(
  2110. _u("Rejit: function: %s (%s), bailOutCount: %hu"),
  2111. executeFunction->GetDisplayName(),
  2112. executeFunction->GetDebugNumberSet(debugStringBuffer),
  2113. bailOutRecord->bailOutCount);
  2114. Output::Print(_u(" callCount: %u"), callsCount);
  2115. Output::Print(_u(" reason: %S"), GetRejitReasonName(rejitReason));
  2116. if(bailOutKind != IR::BailOutInvalid)
  2117. {
  2118. Output::Print(_u(" (%S)"), ::GetBailOutKindName(bailOutKind));
  2119. }
  2120. Output::Print(_u("\n"));
  2121. Output::Flush();
  2122. }
  2123. #endif
  2124. }
  2125. }
  2126. // To avoid always incurring the cost of collecting runtime stats (valid bailOutKind),
  2127. // the initial codegen'd version of a loop body does not collect them. After a second bailout we rejit the body
  2128. // with runtime stats collection. On subsequent bailouts we can evaluate our heuristics.
  2129. void BailOutRecord::ScheduleLoopBodyCodeGen(Js::ScriptFunction * function, Js::ScriptFunction * innerMostInlinee, BailOutRecord const * bailOutRecord, IR::BailOutKind bailOutKind)
  2130. {
  2131. Assert(bailOutKind != IR::LazyBailOut);
  2132. Js::FunctionBody * executeFunction = function->GetFunctionBody();
  2133. if (PHASE_OFF(Js::ReJITPhase, executeFunction))
  2134. {
  2135. return;
  2136. }
  2137. Js::LoopHeader * loopHeader = nullptr;
  2138. Js::InterpreterStackFrame * interpreterFrame = executeFunction->GetScriptContext()->GetThreadContext()->GetLeafInterpreterFrame();
  2139. loopHeader = executeFunction->GetLoopHeader(interpreterFrame->GetCurrentLoopNum());
  2140. Assert(loopHeader != nullptr);
  2141. BailOutRecord * bailOutRecordNotConst = (BailOutRecord *)(void *)bailOutRecord;
  2142. bailOutRecordNotConst->bailOutCount++;
  2143. RejitReason rejitReason = RejitReason::None;
  2144. Assert(bailOutKind != IR::BailOutInvalid);
  2145. Js::LoopEntryPointInfo* entryPointInfo = loopHeader->GetCurrentEntryPointInfo();
  2146. entryPointInfo->totalJittedLoopIterations += entryPointInfo->jittedLoopIterationsSinceLastBailout;
  2147. entryPointInfo->jittedLoopIterationsSinceLastBailout = 0;
  2148. uint8 totalJittedLoopIterations = entryPointInfo->totalJittedLoopIterations > 255 ? 255 : static_cast<uint8>(entryPointInfo->totalJittedLoopIterations);
  2149. totalJittedLoopIterations = totalJittedLoopIterations <= Js::LoopEntryPointInfo::GetDecrLoopCountPerBailout() ? 0 : totalJittedLoopIterations - Js::LoopEntryPointInfo::GetDecrLoopCountPerBailout();
  2150. CheckPreemptiveRejit(executeFunction, bailOutKind, bailOutRecordNotConst, totalJittedLoopIterations, interpreterFrame->GetCurrentLoopNum());
  2151. entryPointInfo->totalJittedLoopIterations = totalJittedLoopIterations;
  2152. if ((executeFunction->HasDynamicProfileInfo() && totalJittedLoopIterations == 0) ||
  2153. PHASE_FORCE(Js::ReJITPhase, executeFunction))
  2154. {
  2155. Js::DynamicProfileInfo * profileInfo = executeFunction->GetAnyDynamicProfileInfo();
  2156. if ((bailOutKind & (IR::BailOutOnResultConditions | IR::BailOutOnDivSrcConditions)) || bailOutKind == IR::BailOutIntOnly || bailOutKind == IR::BailOnIntMin)
  2157. {
  2158. if (bailOutKind & IR::BailOutOnMulOverflow)
  2159. {
  2160. profileInfo->DisableAggressiveMulIntTypeSpec(true);
  2161. rejitReason = RejitReason::AggressiveMulIntTypeSpecDisabled;
  2162. }
  2163. else if ((bailOutKind & (IR::BailOutOnDivByZero | IR::BailOutOnDivOfMinInt)) || bailOutKind == IR::BailOnDivResultNotInt)
  2164. {
  2165. profileInfo->DisableDivIntTypeSpec(true);
  2166. rejitReason = RejitReason::DivIntTypeSpecDisabled;
  2167. }
  2168. else
  2169. {
  2170. profileInfo->DisableAggressiveIntTypeSpec(true);
  2171. rejitReason = RejitReason::AggressiveIntTypeSpecDisabled;
  2172. }
  2173. executeFunction->SetDontRethunkAfterBailout();
  2174. }
  2175. else switch(bailOutKind)
  2176. {
  2177. case IR::BailOutOnNotPrimitive:
  2178. profileInfo->DisableLossyIntTypeSpec();
  2179. executeFunction->SetDontRethunkAfterBailout();
  2180. rejitReason = RejitReason::LossyIntTypeSpecDisabled;
  2181. break;
  2182. case IR::BailOutOnMemOpError:
  2183. profileInfo->DisableMemOp();
  2184. executeFunction->SetDontRethunkAfterBailout();
  2185. rejitReason = RejitReason::MemOpDisabled;
  2186. break;
  2187. case IR::BailOutPrimitiveButString:
  2188. case IR::BailOutNumberOnly:
  2189. profileInfo->DisableFloatTypeSpec();
  2190. executeFunction->SetDontRethunkAfterBailout();
  2191. rejitReason = RejitReason::FloatTypeSpecDisabled;
  2192. break;
  2193. case IR::BailOutOnImplicitCalls:
  2194. case IR::BailOutOnImplicitCallsPreOp:
  2195. rejitReason = RejitReason::ImplicitCallFlagsChanged;
  2196. break;
  2197. case IR::BailOutExpectingInteger:
  2198. profileInfo->DisableSwitchOpt();
  2199. executeFunction->SetDontRethunkAfterBailout();
  2200. rejitReason = RejitReason::DisableSwitchOptExpectingInteger;
  2201. break;
  2202. case IR::BailOutExpectingString:
  2203. profileInfo->DisableSwitchOpt();
  2204. executeFunction->SetDontRethunkAfterBailout();
  2205. rejitReason = RejitReason::DisableSwitchOptExpectingString;
  2206. break;
  2207. case IR::BailOnStackArgsOutOfActualsRange:
  2208. AssertMsg(false, "How did we reach here ? Stack args opt is currently disabled in loop body gen.");
  2209. break;
  2210. case IR::BailOnModByPowerOf2:
  2211. rejitReason = RejitReason::ModByPowerOf2;
  2212. break;
  2213. case IR::BailOutOnNotArray:
  2214. profileInfo->DisableArrayCheckHoist(true);
  2215. executeFunction->SetDontRethunkAfterBailout();
  2216. rejitReason = RejitReason::ArrayCheckHoistDisabled;
  2217. break;
  2218. case IR::BailOutOnNotNativeArray:
  2219. // REVIEW: We have an issue with array profile info. The info on the type of array we have won't
  2220. // get fixed by rejitting. For now, just give up after 50 rejits.
  2221. if (loopHeader->GetRejitCount() >= 50)
  2222. {
  2223. rejitReason = RejitReason::None;
  2224. }
  2225. else
  2226. {
  2227. rejitReason = RejitReason::ExpectingNativeArray;
  2228. }
  2229. break;
  2230. case IR::BailOutConvertedNativeArray:
  2231. rejitReason = RejitReason::ConvertedNativeArray;
  2232. break;
  2233. case IR::BailOutConventionalTypedArrayAccessOnly:
  2234. profileInfo->DisableTypedArrayTypeSpec(true);
  2235. executeFunction->SetDontRethunkAfterBailout();
  2236. rejitReason = RejitReason::TypedArrayTypeSpecDisabled;
  2237. break;
  2238. case IR::BailOutConventionalNativeArrayAccessOnly:
  2239. rejitReason = RejitReason::ExpectingConventionalNativeArrayAccess;
  2240. break;
  2241. case IR::BailOutOnMissingValue:
  2242. profileInfo->DisableArrayMissingValueCheckHoist(true);
  2243. rejitReason = RejitReason::ArrayMissingValueCheckHoistDisabled;
  2244. break;
  2245. case IR::BailOutOnArrayAccessHelperCall:
  2246. // This is a pre-op bailout, so the interpreter will update the profile data for this byte-code instruction to
  2247. // prevent excessive bailouts here in the future
  2248. rejitReason = RejitReason::ArrayAccessNeededHelperCall;
  2249. break;
  2250. case IR::BailOutOnInvalidatedArrayHeadSegment:
  2251. profileInfo->DisableJsArraySegmentHoist(true);
  2252. executeFunction->SetDontRethunkAfterBailout();
  2253. rejitReason = RejitReason::JsArraySegmentHoistDisabled;
  2254. break;
  2255. case IR::BailOutOnIrregularLength:
  2256. profileInfo->DisableLdLenIntSpec();
  2257. executeFunction->SetDontRethunkAfterBailout();
  2258. rejitReason = RejitReason::LdLenIntSpecDisabled;
  2259. break;
  2260. case IR::BailOutOnFailedHoistedBoundCheck:
  2261. profileInfo->DisableBoundCheckHoist(true);
  2262. executeFunction->SetDontRethunkAfterBailout();
  2263. rejitReason = RejitReason::BoundCheckHoistDisabled;
  2264. break;
  2265. case IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck:
  2266. profileInfo->DisableLoopCountBasedBoundCheckHoist(true);
  2267. executeFunction->SetDontRethunkAfterBailout();
  2268. rejitReason = RejitReason::LoopCountBasedBoundCheckHoistDisabled;
  2269. break;
  2270. case IR::BailOutOnInlineFunction:
  2271. case IR::BailOutOnPolymorphicInlineFunction:
  2272. case IR::BailOutOnFailedPolymorphicInlineTypeCheck:
  2273. rejitReason = RejitReason::InlineeChanged;
  2274. break;
  2275. case IR::BailOutOnNoProfile:
  2276. rejitReason = RejitReason::NoProfile;
  2277. executeFunction->ResetBailOnMisingProfileCount();
  2278. break;
  2279. case IR::BailOutCheckThis:
  2280. profileInfo->DisableCheckThis();
  2281. executeFunction->SetDontRethunkAfterBailout();
  2282. rejitReason = RejitReason::CheckThisDisabled;
  2283. break;
  2284. case IR::BailOutFailedTypeCheck:
  2285. // An inline cache must have gone from monomorphic to polymorphic.
  2286. // This is already noted in the profile data, so optimization of the given ld/st will
  2287. // be inhibited on re-jit.
  2288. // Consider disabling the optimization across the function after n failed type checks.
  2289. // Disable ObjTypeSpec in a large loop body after the first rejit itself.
  2290. // Rejitting a large loop body takes more time and the fact that loop bodies are prioritized ahead of functions to be jitted only augments the problem.
  2291. if(executeFunction->GetByteCodeInLoopCount() > (uint)CONFIG_FLAG(LoopBodySizeThresholdToDisableOpts))
  2292. {
  2293. profileInfo->DisableObjTypeSpecInJitLoopBody();
  2294. if(PHASE_TRACE1(Js::DisabledObjTypeSpecPhase))
  2295. {
  2296. Output::Print(_u("Disabled obj type spec in jit loop body for loop %d in %s (%d)\n"),
  2297. executeFunction->GetLoopNumber(loopHeader), executeFunction->GetDisplayName(), executeFunction->GetFunctionNumber());
  2298. Output::Flush();
  2299. }
  2300. }
  2301. rejitReason = RejitReason::FailedTypeCheck;
  2302. break;
  2303. case IR::BailOutFailedFixedFieldTypeCheck:
  2304. // An inline cache must have gone from monomorphic to polymorphic or some fixed field
  2305. // became non-fixed. Either one is already noted in the profile data and type system,
  2306. // so optimization of the given instruction will be inhibited on re-jit.
  2307. // Consider disabling the optimization across the function after n failed type checks.
  2308. rejitReason = RejitReason::FailedFixedFieldTypeCheck;
  2309. break;
  2310. case IR::BailOutFailedEquivalentTypeCheck:
  2311. case IR::BailOutFailedEquivalentFixedFieldTypeCheck:
  2312. rejitReason = bailOutKind == IR::BailOutFailedEquivalentTypeCheck ?
  2313. RejitReason::FailedEquivalentTypeCheck : RejitReason::FailedEquivalentFixedFieldTypeCheck;
  2314. break;
  2315. case IR::BailOutFailedCtorGuardCheck:
  2316. // (ObjTypeSpec): Consider scheduling re-JIT right after the first bailout. We will never successfully execute the
  2317. // function from which we just bailed out, unless we take a different code path through it.
  2318. // A constructor cache guard may be invalidated for one of two reasons:
  2319. // a) the constructor's prototype property has changed, or
  2320. // b) one of the properties protected by the guard (this constructor cache served as) has changed in some way (e.g. became
  2321. // read-only).
  2322. // In the former case, the cache itself will be marked as polymorphic and on re-JIT we won't do the optimization.
  2323. // In the latter case, the inline cache for the offending property will be cleared and on re-JIT the guard will not be enlisted
  2324. // to protect that property operation.
  2325. rejitReason = RejitReason::CtorGuardInvalidated;
  2326. break;
  2327. case IR::BailOutOnFloor:
  2328. {
  2329. profileInfo->DisableFloorInlining();
  2330. rejitReason = RejitReason::FloorInliningDisabled;
  2331. break;
  2332. }
  2333. case IR::BailOutFailedFixedFieldCheck:
  2334. rejitReason = RejitReason::FailedFixedFieldCheck;
  2335. break;
  2336. case IR::BailOutOnTaggedValue:
  2337. profileInfo->DisableTagCheck();
  2338. executeFunction->SetDontRethunkAfterBailout();
  2339. rejitReason = RejitReason::FailedTagCheck;
  2340. break;
  2341. case IR::BailOutOnPowIntIntOverflow:
  2342. profileInfo->DisablePowIntIntTypeSpec();
  2343. executeFunction->SetDontRethunkAfterBailout();
  2344. rejitReason = RejitReason::PowIntIntTypeSpecDisabled;
  2345. break;
  2346. }
  2347. if(PHASE_FORCE(Js::ReJITPhase, executeFunction) && rejitReason == RejitReason::None)
  2348. {
  2349. rejitReason = RejitReason::Forced;
  2350. }
  2351. }
  2352. if (PHASE_FORCE(Js::ReJITPhase, executeFunction) && rejitReason == RejitReason::None)
  2353. {
  2354. rejitReason = RejitReason::Forced;
  2355. }
  2356. REJIT_KIND_TESTTRACE(bailOutKind, _u("Bailout from loop: function: %s, loopNumber: %d, bailOutKindName: (%S), reJitReason: %S\r\n"),
  2357. function->GetFunctionBody()->GetDisplayName(), executeFunction->GetLoopNumber(loopHeader),
  2358. ::GetBailOutKindName(bailOutKind), GetRejitReasonName(rejitReason));
  2359. #ifdef REJIT_STATS
  2360. executeFunction->GetScriptContext()->LogBailout(executeFunction, bailOutKind);
  2361. #endif
  2362. if (rejitReason != RejitReason::None)
  2363. {
  2364. #ifdef REJIT_STATS
  2365. executeFunction->GetScriptContext()->LogRejit(executeFunction, rejitReason);
  2366. #endif
  2367. // Single bailout triggers re-JIT of loop body. the actual codegen scheduling of the new
  2368. // loop body happens in the interpreter
  2369. loopHeader->interpretCount = executeFunction->GetLoopInterpretCount(loopHeader) - 2;
  2370. loopHeader->CreateEntryPoint();
  2371. loopHeader->IncRejitCount();
  2372. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2373. if(PHASE_TRACE(Js::ReJITPhase, executeFunction))
  2374. {
  2375. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2376. Output::Print(
  2377. _u("Rejit(loop): function: %s (%s) loop: %u bailOutCount: %hu reason: %S"),
  2378. executeFunction->GetDisplayName(),
  2379. executeFunction->GetDebugNumberSet(debugStringBuffer),
  2380. executeFunction->GetLoopNumber(loopHeader),
  2381. bailOutRecord->bailOutCount,
  2382. GetRejitReasonName(rejitReason));
  2383. if(bailOutKind != IR::BailOutInvalid)
  2384. {
  2385. Output::Print(_u(" (%S)"), ::GetBailOutKindName(bailOutKind));
  2386. }
  2387. Output::Print(_u("\n"));
  2388. Output::Flush();
  2389. }
  2390. #endif
  2391. }
  2392. }
  2393. void BailOutRecord::CheckPreemptiveRejit(Js::FunctionBody* executeFunction, IR::BailOutKind bailOutKind, BailOutRecord* bailoutRecord, uint8& callsOrIterationsCount, int loopNumber)
  2394. {
  2395. if (bailOutKind == IR::BailOutOnNoProfile && executeFunction->IncrementBailOnMisingProfileCount() > CONFIG_FLAG(BailOnNoProfileLimit))
  2396. {
  2397. // A rejit here should improve code quality, so lets avoid too many unnecessary bailouts.
  2398. executeFunction->ResetBailOnMisingProfileCount();
  2399. bailoutRecord->bailOutCount = 0;
  2400. callsOrIterationsCount = 0;
  2401. }
  2402. else if (bailoutRecord->bailOutCount > CONFIG_FLAG(RejitMaxBailOutCount))
  2403. {
  2404. switch (bailOutKind)
  2405. {
  2406. case IR::BailOutOnPolymorphicInlineFunction:
  2407. case IR::BailOutOnFailedPolymorphicInlineTypeCheck:
  2408. case IR::BailOutFailedInlineTypeCheck:
  2409. case IR::BailOutOnInlineFunction:
  2410. case IR::BailOutFailedTypeCheck:
  2411. case IR::BailOutFailedFixedFieldTypeCheck:
  2412. case IR::BailOutFailedCtorGuardCheck:
  2413. case IR::BailOutFailedFixedFieldCheck:
  2414. case IR::BailOutFailedEquivalentTypeCheck:
  2415. case IR::BailOutFailedEquivalentFixedFieldTypeCheck:
  2416. {
  2417. // If we consistently see RejitMaxBailOutCount bailouts for these kinds, then likely we have stale profile data and it is beneficial to rejit.
  2418. // Note you need to include only bailout kinds which don't disable the entire optimizations.
  2419. if (loopNumber == -1)
  2420. {
  2421. REJIT_KIND_TESTTRACE(bailOutKind, _u("Force rejit as RejitMaxBailoOutCount reached for a bailout record: function: %s, bailOutKindName: (%S), bailOutCount: %d, callCount: %d RejitMaxBailoutCount: %d\r\n"),
  2422. executeFunction->GetDisplayName(), ::GetBailOutKindName(bailOutKind), bailoutRecord->bailOutCount, callsOrIterationsCount, CONFIG_FLAG(RejitMaxBailOutCount));
  2423. }
  2424. else
  2425. {
  2426. REJIT_KIND_TESTTRACE(bailOutKind, _u("Force rejit as RejitMaxBailoOutCount reached for a bailout record: function: %s, loopNumber: %d, bailOutKindName: (%S), bailOutCount: %d, callCount: %d RejitMaxBailoutCount: %d\r\n"),
  2427. executeFunction->GetDisplayName(), loopNumber, ::GetBailOutKindName(bailOutKind), bailoutRecord->bailOutCount, callsOrIterationsCount, CONFIG_FLAG(RejitMaxBailOutCount));
  2428. }
  2429. bailoutRecord->bailOutCount = 0;
  2430. callsOrIterationsCount = 0;
  2431. break;
  2432. }
  2433. default: break;
  2434. }
  2435. }
  2436. }
  2437. Js::Var BailOutRecord::BailOutForElidedYield(void * framePointer)
  2438. {
  2439. Js::JavascriptCallStackLayout * const layout = Js::JavascriptCallStackLayout::FromFramePointer(framePointer);
  2440. Js::ScriptFunction ** functionRef = (Js::ScriptFunction **)&layout->functionObject;
  2441. Js::ScriptFunction * function = *functionRef;
  2442. Js::FunctionBody * executeFunction = function->GetFunctionBody();
  2443. bool isInDebugMode = executeFunction->IsInDebugMode();
  2444. Js::JavascriptGenerator* generator = static_cast<Js::JavascriptGenerator*>(layout->args[0]);
  2445. Js::InterpreterStackFrame* frame = generator->GetFrame();
  2446. ThreadContext *threadContext = frame->GetScriptContext()->GetThreadContext();
  2447. Js::ResumeYieldData* resumeYieldData = static_cast<Js::ResumeYieldData*>(layout->args[1]);
  2448. frame->SetNonVarReg(executeFunction->GetYieldRegister(), resumeYieldData);
  2449. // The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
  2450. // give the InterpreterStackFrame a legit enough stack address to make this comparison work.
  2451. frame->m_stackAddress = reinterpret_cast<DWORD_PTR>(&generator);
  2452. executeFunction->BeginExecution();
  2453. Js::Var aReturn = nullptr;
  2454. {
  2455. // Following _AddressOfReturnAddress <= real address of "returnAddress". Suffices for RemoteStackWalker to test partially initialized interpreter frame.
  2456. Js::InterpreterStackFrame::PushPopFrameHelper pushPopFrameHelper(frame, _ReturnAddress(), _AddressOfReturnAddress());
  2457. aReturn = isInDebugMode ? frame->DebugProcess() : frame->Process();
  2458. // Note: in debug mode we always have to bailout to debug thunk,
  2459. // as normal interpreter thunk expects byte code compiled w/o debugging.
  2460. }
  2461. executeFunction->EndExecution();
  2462. if (executeFunction->HasDynamicProfileInfo())
  2463. {
  2464. Js::DynamicProfileInfo *dynamicProfileInfo = executeFunction->GetAnyDynamicProfileInfo();
  2465. dynamicProfileInfo->RecordImplicitCallFlags(threadContext->GetImplicitCallFlags());
  2466. }
  2467. return aReturn;
  2468. }
  2469. BranchBailOutRecord::BranchBailOutRecord(uint32 trueBailOutOffset, uint32 falseBailOutOffset, Js::RegSlot resultByteCodeReg, IR::BailOutKind kind, Func * bailOutFunc)
  2470. : BailOutRecord(trueBailOutOffset, (uint)-1, kind, bailOutFunc), falseBailOutOffset(falseBailOutOffset)
  2471. {
  2472. branchValueRegSlot = resultByteCodeReg;
  2473. type = BailoutRecordType::Branch;
  2474. };
  2475. Js::Var BranchBailOutRecord::BailOut(BranchBailOutRecord const * bailOutRecord, BOOL cond)
  2476. {
  2477. Assert(bailOutRecord);
  2478. void * argoutRestoreAddr = nullptr;
  2479. #ifdef _M_IX86
  2480. void * addressOfRetAddress = _AddressOfReturnAddress();
  2481. if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0 || bailOutRecord->ehBailoutData->finallyOffset != 0 || bailOutRecord->ehBailoutData->ht == Js::HandlerType::HT_Finally))
  2482. {
  2483. argoutRestoreAddr = (void *)((char*)addressOfRetAddress + ((2 + 1) * MachPtr)); // Account for the parameters and return address of this function
  2484. }
  2485. #endif
  2486. Js::JavascriptCallStackLayout *const layout = bailOutRecord->GetStackLayout();
  2487. Js::ScriptFunction * function = (Js::ScriptFunction *)layout->functionObject;
  2488. if (bailOutRecord->bailOutKind == IR::BailOutOnImplicitCalls)
  2489. {
  2490. function->GetScriptContext()->GetThreadContext()->CheckAndResetImplicitCallAccessorFlag();
  2491. }
  2492. Js::ImplicitCallFlags savedImplicitCallFlags = function->GetScriptContext()->GetThreadContext()->GetImplicitCallFlags();
  2493. if(bailOutRecord->globalBailOutRecordTable->isLoopBody)
  2494. {
  2495. if (bailOutRecord->globalBailOutRecordTable->isInlinedFunction)
  2496. {
  2497. return reinterpret_cast<Js::Var>(BailOutFromLoopBodyInlined(layout, bailOutRecord, cond, _ReturnAddress()));
  2498. }
  2499. return reinterpret_cast<Js::Var>(BailOutFromLoopBody(layout, bailOutRecord, cond));
  2500. }
  2501. if(bailOutRecord->globalBailOutRecordTable->isInlinedFunction)
  2502. {
  2503. return BailOutInlined(layout, bailOutRecord, cond, _ReturnAddress(), savedImplicitCallFlags);
  2504. }
  2505. return BailOutFromFunction(layout, bailOutRecord, cond, _ReturnAddress(), argoutRestoreAddr, savedImplicitCallFlags);
  2506. }
  2507. Js::Var
  2508. BranchBailOutRecord::BailOutFromFunction(Js::JavascriptCallStackLayout * layout, BranchBailOutRecord const * bailOutRecord, BOOL cond, void * returnAddress, void * argoutRestoreAddress, Js::ImplicitCallFlags savedImplicitCallFlags)
  2509. {
  2510. Assert(bailOutRecord->parent == nullptr);
  2511. uint32 bailOutOffset = cond? bailOutRecord->bailOutOffset : bailOutRecord->falseBailOutOffset;
  2512. Js::Var branchValue = nullptr;
  2513. if (bailOutRecord->branchValueRegSlot != Js::Constants::NoRegister)
  2514. {
  2515. Js::ScriptContext *scriptContext = layout->functionObject->GetScriptContext();
  2516. branchValue = (cond ? scriptContext->GetLibrary()->GetTrue() : scriptContext->GetLibrary()->GetFalse());
  2517. }
  2518. return __super::BailOutCommon(layout, bailOutRecord, bailOutOffset, returnAddress, bailOutRecord->bailOutKind, savedImplicitCallFlags, branchValue, nullptr, argoutRestoreAddress);
  2519. }
  2520. uint32
  2521. BranchBailOutRecord::BailOutFromLoopBody(Js::JavascriptCallStackLayout * layout, BranchBailOutRecord const * bailOutRecord, BOOL cond)
  2522. {
  2523. Assert(bailOutRecord->parent == nullptr);
  2524. uint32 bailOutOffset = cond? bailOutRecord->bailOutOffset : bailOutRecord->falseBailOutOffset;
  2525. Js::Var branchValue = nullptr;
  2526. if (bailOutRecord->branchValueRegSlot != Js::Constants::NoRegister)
  2527. {
  2528. Js::ScriptContext *scriptContext = layout->functionObject->GetScriptContext();
  2529. branchValue = (cond ? scriptContext->GetLibrary()->GetTrue() : scriptContext->GetLibrary()->GetFalse());
  2530. }
  2531. return __super::BailOutFromLoopBodyCommon(layout, bailOutRecord, bailOutOffset, bailOutRecord->bailOutKind, branchValue);
  2532. }
  2533. Js::Var
  2534. BranchBailOutRecord::BailOutInlined(Js::JavascriptCallStackLayout * layout, BranchBailOutRecord const * bailOutRecord, BOOL cond, void * returnAddress, Js::ImplicitCallFlags savedImplicitCallFlags)
  2535. {
  2536. Assert(bailOutRecord->parent != nullptr);
  2537. uint32 bailOutOffset = cond? bailOutRecord->bailOutOffset : bailOutRecord->falseBailOutOffset;
  2538. Js::Var branchValue = nullptr;
  2539. if (bailOutRecord->branchValueRegSlot != Js::Constants::NoRegister)
  2540. {
  2541. Js::ScriptContext *scriptContext = layout->functionObject->GetScriptContext();
  2542. branchValue = (cond ? scriptContext->GetLibrary()->GetTrue() : scriptContext->GetLibrary()->GetFalse());
  2543. }
  2544. return __super::BailOutInlinedCommon(layout, bailOutRecord, bailOutOffset, returnAddress, bailOutRecord->bailOutKind, savedImplicitCallFlags, branchValue);
  2545. }
  2546. uint32
  2547. BranchBailOutRecord::BailOutFromLoopBodyInlined(Js::JavascriptCallStackLayout * layout, BranchBailOutRecord const * bailOutRecord, BOOL cond, void * returnAddress)
  2548. {
  2549. Assert(bailOutRecord->parent != nullptr);
  2550. uint32 bailOutOffset = cond? bailOutRecord->bailOutOffset : bailOutRecord->falseBailOutOffset;
  2551. Js::Var branchValue = nullptr;
  2552. if (bailOutRecord->branchValueRegSlot != Js::Constants::NoRegister)
  2553. {
  2554. Js::ScriptContext *scriptContext = layout->functionObject->GetScriptContext();
  2555. branchValue = (cond ? scriptContext->GetLibrary()->GetTrue() : scriptContext->GetLibrary()->GetFalse());
  2556. }
  2557. return __super::BailOutFromLoopBodyInlinedCommon(layout, bailOutRecord, bailOutOffset, returnAddress, bailOutRecord->bailOutKind, branchValue);
  2558. }
  2559. SharedBailOutRecord::SharedBailOutRecord(uint32 bailOutOffset, uint bailOutCacheIndex, IR::BailOutKind kind, Func *bailOutFunc)
  2560. : BailOutRecord(bailOutOffset, bailOutCacheIndex, kind, bailOutFunc)
  2561. {
  2562. this->functionBody = nullptr;
  2563. this->type = BailoutRecordType::Shared;
  2564. }
  2565. void LazyBailOutRecord::SetBailOutKind()
  2566. {
  2567. this->bailoutRecord->SetBailOutKind(IR::BailOutKind::LazyBailOut);
  2568. }
  2569. #if DBG
  2570. void LazyBailOutRecord::Dump(Js::FunctionBody* functionBody)
  2571. {
  2572. OUTPUT_PRINT(functionBody);
  2573. Output::Print(_u("Bytecode Offset: #%04x opcode: %s"), this->bailoutRecord->GetBailOutOffset(), Js::OpCodeUtil::GetOpCodeName(this->bailoutRecord->GetBailOutOpCode()));
  2574. }
  2575. #endif
  2576. void GlobalBailOutRecordDataTable::Finalize(NativeCodeData::Allocator *allocator, JitArenaAllocator *tempAlloc)
  2577. {
  2578. GlobalBailOutRecordDataRow *newRows = NativeCodeDataNewArrayZNoFixup(allocator, GlobalBailOutRecordDataRow, length);
  2579. memcpy(newRows, globalBailOutRecordDataRows, sizeof(GlobalBailOutRecordDataRow) * length);
  2580. JitAdeleteArray(tempAlloc, length, globalBailOutRecordDataRows);
  2581. globalBailOutRecordDataRows = newRows;
  2582. size = length;
  2583. #if DBG
  2584. if (length > 0)
  2585. {
  2586. uint32 currStart = globalBailOutRecordDataRows[0].start;
  2587. for (uint32 i = 1; i < length; i++)
  2588. {
  2589. AssertMsg(currStart <= globalBailOutRecordDataRows[i].start,
  2590. "Rows in the table must be in order by start ID");
  2591. currStart = globalBailOutRecordDataRows[i].start;
  2592. }
  2593. }
  2594. #endif
  2595. }
  2596. void GlobalBailOutRecordDataTable::AddOrUpdateRow(JitArenaAllocator *allocator, uint32 bailOutRecordId, uint32 regSlot, bool isFloat, bool isInt,
  2597. bool isSimd128F4, bool isSimd128I4, bool isSimd128I8, bool isSimd128I16, bool isSimd128U4, bool isSimd128U8, bool isSimd128U16,
  2598. bool isSimd128B4, bool isSimd128B8, bool isSimd128B16, int32 offset, uint *lastUpdatedRowIndex)
  2599. {
  2600. Assert(offset != 0);
  2601. const int INITIAL_TABLE_SIZE = 64;
  2602. if (size == 0)
  2603. {
  2604. Assert(length == 0);
  2605. size = INITIAL_TABLE_SIZE;
  2606. globalBailOutRecordDataRows = JitAnewArrayZ(allocator, GlobalBailOutRecordDataRow, size);
  2607. }
  2608. Assert(lastUpdatedRowIndex != nullptr);
  2609. if ((*lastUpdatedRowIndex) != -1)
  2610. {
  2611. GlobalBailOutRecordDataRow *rowToUpdate = &globalBailOutRecordDataRows[(*lastUpdatedRowIndex)];
  2612. if(rowToUpdate->offset == offset &&
  2613. rowToUpdate->isInt == (unsigned)isInt &&
  2614. rowToUpdate->isFloat == (unsigned)isFloat &&
  2615. #ifdef ENABLE_SIMDJS
  2616. // SIMD_JS
  2617. rowToUpdate->isSimd128F4 == (unsigned) isSimd128F4 &&
  2618. rowToUpdate->isSimd128I4 == (unsigned) isSimd128I4 &&
  2619. rowToUpdate->isSimd128I8 == (unsigned) isSimd128I8 &&
  2620. rowToUpdate->isSimd128I16 == (unsigned) isSimd128I16 &&
  2621. rowToUpdate->isSimd128U4 == (unsigned) isSimd128U4 &&
  2622. rowToUpdate->isSimd128U8 == (unsigned) isSimd128U8 &&
  2623. rowToUpdate->isSimd128U16 == (unsigned) isSimd128U16 &&
  2624. rowToUpdate->isSimd128B4 == (unsigned) isSimd128B4 &&
  2625. rowToUpdate->isSimd128B8 == (unsigned) isSimd128B8 &&
  2626. rowToUpdate->isSimd128B16 == (unsigned) isSimd128B16 &&
  2627. #endif
  2628. rowToUpdate->end + 1 == bailOutRecordId)
  2629. {
  2630. Assert(rowToUpdate->regSlot == regSlot);
  2631. rowToUpdate->end = bailOutRecordId;
  2632. return;
  2633. }
  2634. }
  2635. if (length == size)
  2636. {
  2637. size = length << 1;
  2638. globalBailOutRecordDataRows = (GlobalBailOutRecordDataRow *)allocator->Realloc(globalBailOutRecordDataRows, length * sizeof(GlobalBailOutRecordDataRow), size * sizeof(GlobalBailOutRecordDataRow));
  2639. }
  2640. GlobalBailOutRecordDataRow *rowToInsert = &globalBailOutRecordDataRows[length];
  2641. rowToInsert->start = bailOutRecordId;
  2642. rowToInsert->end = bailOutRecordId;
  2643. rowToInsert->offset = offset;
  2644. rowToInsert->isFloat = isFloat;
  2645. rowToInsert->isInt = isInt;
  2646. #ifdef ENABLE_SIMDJS
  2647. // SIMD_JS
  2648. rowToInsert->isSimd128F4 = isSimd128F4;
  2649. rowToInsert->isSimd128I4 = isSimd128I4;
  2650. rowToInsert->isSimd128I8 = isSimd128I8 ;
  2651. rowToInsert->isSimd128I16 = isSimd128I16;
  2652. rowToInsert->isSimd128U4 = isSimd128U4 ;
  2653. rowToInsert->isSimd128U8 = isSimd128U8 ;
  2654. rowToInsert->isSimd128U16 = isSimd128U16;
  2655. rowToInsert->isSimd128B4 = isSimd128B4 ;
  2656. rowToInsert->isSimd128B8 = isSimd128B8 ;
  2657. rowToInsert->isSimd128B16 = isSimd128B16;
  2658. #endif
  2659. rowToInsert->regSlot = regSlot;
  2660. *lastUpdatedRowIndex = length++;
  2661. }