BailOut.cpp 136 KB

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