BailOut.cpp 132 KB

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