NativeCodeGenerator.cpp 145 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. #include "Base/ScriptContextProfiler.h"
  7. #if DBG
  8. Js::JavascriptMethod checkCodeGenThunk;
  9. #endif
  10. #ifdef ENABLE_PREJIT
  11. #define IS_PREJIT_ON() (Js::Configuration::Global.flags.Prejit)
  12. #else
  13. #define IS_PREJIT_ON() (DEFAULT_CONFIG_Prejit)
  14. #endif
  15. #define ASSERT_THREAD() AssertMsg(mainThreadId == GetCurrentThreadContextId(), \
  16. "Cannot use this member of native code generator from thread other than the creating context's current thread")
  17. NativeCodeGenerator::NativeCodeGenerator(Js::ScriptContext * scriptContext)
  18. : JsUtil::WaitableJobManager(scriptContext->GetThreadContext()->GetJobProcessor()),
  19. scriptContext(scriptContext),
  20. pendingCodeGenWorkItems(0),
  21. queuedFullJitWorkItemCount(0),
  22. foregroundAllocators(nullptr),
  23. backgroundAllocators(nullptr),
  24. byteCodeSizeGenerated(0),
  25. isClosed(false),
  26. isOptimizedForManyInstances(scriptContext->GetThreadContext()->IsOptimizedForManyInstances()),
  27. SetNativeEntryPoint(Js::FunctionBody::DefaultSetNativeEntryPoint),
  28. freeLoopBodyManager(scriptContext->GetThreadContext()->GetJobProcessor()),
  29. hasUpdatedQForDebugMode(false)
  30. #ifdef PROFILE_EXEC
  31. , foregroundCodeGenProfiler(nullptr)
  32. , backgroundCodeGenProfiler(nullptr)
  33. #endif
  34. {
  35. freeLoopBodyManager.SetNativeCodeGen(this);
  36. #if DBG_DUMP
  37. if (Js::Configuration::Global.flags.IsEnabled(Js::AsmDumpModeFlag)
  38. && (Js::Configuration::Global.flags.AsmDumpMode != nullptr))
  39. {
  40. bool fileOpened = false;
  41. fileOpened = (0 == _wfopen_s(&this->asmFile, Js::Configuration::Global.flags.AsmDumpMode, _u("wt")));
  42. if (!fileOpened)
  43. {
  44. size_t len = wcslen(Js::Configuration::Global.flags.AsmDumpMode);
  45. if (len < _MAX_PATH - 5)
  46. {
  47. char16 filename[_MAX_PATH];
  48. wcscpy_s(filename, _MAX_PATH, Js::Configuration::Global.flags.AsmDumpMode);
  49. char16 * number = filename + len;
  50. for (int i = 0; i < 1000; i++)
  51. {
  52. _itow_s(i, number, 5, 10);
  53. fileOpened = (0 == _wfopen_s(&this->asmFile, filename, _u("wt")));
  54. if (fileOpened)
  55. {
  56. break;
  57. }
  58. }
  59. }
  60. if (!fileOpened)
  61. {
  62. this->asmFile = nullptr;
  63. AssertMsg(0, "Could not open file for AsmDump. The output will goto standard console");
  64. }
  65. }
  66. }
  67. else
  68. {
  69. this->asmFile = nullptr;
  70. }
  71. #endif
  72. #if DBG
  73. this->mainThreadId = GetCurrentThreadContextId();
  74. #endif
  75. Processor()->AddManager(this);
  76. this->freeLoopBodyManager.SetAutoClose(false);
  77. }
  78. NativeCodeGenerator::~NativeCodeGenerator()
  79. {
  80. Assert(this->IsClosed());
  81. #ifdef PROFILE_EXEC
  82. if (this->foregroundCodeGenProfiler != nullptr)
  83. {
  84. this->foregroundCodeGenProfiler->Release();
  85. }
  86. #endif
  87. if(this->foregroundAllocators != nullptr)
  88. {
  89. HeapDelete(this->foregroundAllocators);
  90. }
  91. if (this->backgroundAllocators)
  92. {
  93. #if DBG
  94. // PageAllocator is thread agile. This destructor can be called from background GC thread.
  95. // We have already removed this manager from the job queue and hence its fine to set the threadId to -1.
  96. // We can't DissociatePageAllocator here as its allocated ui thread.
  97. //this->Processor()->DissociatePageAllocator(allocator->GetPageAllocator());
  98. this->backgroundAllocators->ClearConcurrentThreadId();
  99. #endif
  100. // The native code generator may be deleted after Close was called on the job processor. In that case, the
  101. // background thread is no longer running, so clean things up in the foreground.
  102. HeapDelete(this->backgroundAllocators);
  103. }
  104. #ifdef PROFILE_EXEC
  105. if (Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag))
  106. {
  107. while (this->backgroundCodeGenProfiler)
  108. {
  109. Js::ScriptContextProfiler *codegenProfiler = this->backgroundCodeGenProfiler;
  110. this->backgroundCodeGenProfiler = this->backgroundCodeGenProfiler->next;
  111. // background codegen profiler is allocated in background thread,
  112. // clear the thead Id before release
  113. #ifdef DBG
  114. if (codegenProfiler->pageAllocator != nullptr)
  115. {
  116. codegenProfiler->pageAllocator->SetDisableThreadAccessCheck();
  117. }
  118. #endif
  119. codegenProfiler->Release();
  120. }
  121. }
  122. else
  123. {
  124. Assert(this->backgroundCodeGenProfiler == nullptr);
  125. }
  126. #endif
  127. }
  128. void NativeCodeGenerator::Close()
  129. {
  130. Assert(!this->IsClosed());
  131. // Close FreeLoopBodyJobManager first, as it depends on NativeCodeGenerator to be open before it's removed
  132. this->freeLoopBodyManager.Close();
  133. // Remove only if it is not updated in the debug mode (and which goes to interpreter mode).
  134. if (!hasUpdatedQForDebugMode || Js::Configuration::Global.EnableJitInDebugMode())
  135. {
  136. Processor()->RemoveManager(this);
  137. }
  138. this->isClosed = true;
  139. Assert(!queuedFullJitWorkItems.Head());
  140. Assert(queuedFullJitWorkItemCount == 0);
  141. for(JsUtil::Job *job = workItems.Head(); job;)
  142. {
  143. JsUtil::Job *const next = job->Next();
  144. JobProcessed(job, /*succeeded*/ false);
  145. job = next;
  146. }
  147. workItems.Clear();
  148. // Only decommit here instead of releasing the memory, so we retain control over these addresses
  149. // Mitigate against the case the entry point is called after the script site is closed
  150. if (this->backgroundAllocators)
  151. {
  152. this->backgroundAllocators->emitBufferManager.Decommit();
  153. }
  154. if (this->foregroundAllocators)
  155. {
  156. this->foregroundAllocators->emitBufferManager.Decommit();
  157. }
  158. #if DBG_DUMP
  159. if (this->asmFile != nullptr)
  160. {
  161. if(0 != fclose(this->asmFile))
  162. {
  163. AssertMsg(0, "Could not close file for AsmDump. You may ignore this warning.");
  164. }
  165. }
  166. #endif
  167. }
  168. #if DBG_DUMP
  169. extern Func *CurrentFunc;
  170. #endif
  171. JsFunctionCodeGen *
  172. NativeCodeGenerator::NewFunctionCodeGen(Js::FunctionBody *functionBody, Js::EntryPointInfo* info)
  173. {
  174. return HeapNewNoThrow(JsFunctionCodeGen, this, functionBody, info, functionBody->IsInDebugMode());
  175. }
  176. JsLoopBodyCodeGen *
  177. NativeCodeGenerator::NewLoopBodyCodeGen(Js::FunctionBody *functionBody, Js::EntryPointInfo* info, Js::LoopHeader * loopHeader)
  178. {
  179. return HeapNewNoThrow(JsLoopBodyCodeGen, this, functionBody, info, functionBody->IsInDebugMode(), loopHeader);
  180. }
  181. #ifdef ENABLE_PREJIT
  182. bool
  183. NativeCodeGenerator::DoBackEnd(Js::FunctionBody *fn)
  184. {
  185. if (PHASE_OFF(Js::BackEndPhase, fn))
  186. {
  187. return false;
  188. }
  189. if (fn->IsAsmJSModule() || fn->IsGeneratorAndJitIsDisabled())
  190. {
  191. return false;
  192. }
  193. return true;
  194. }
  195. void
  196. NativeCodeGenerator::GenerateAllFunctions(Js::FunctionBody * fn)
  197. {
  198. Assert(IS_PREJIT_ON());
  199. Assert(fn->GetDefaultFunctionEntryPointInfo()->entryPointIndex == 0);
  200. // Make sure this isn't a deferred function
  201. Assert(fn->GetFunctionBody() == fn);
  202. Assert(!fn->IsDeferred());
  203. if (DoBackEnd(fn))
  204. {
  205. if (fn->GetLoopCount() != 0 && fn->ForceJITLoopBody() && !fn->IsInDebugMode())
  206. {
  207. // Only jit the loop body with /force:JITLoopBody
  208. for (uint i = 0; i < fn->GetLoopCount(); i++)
  209. {
  210. Js::LoopHeader * loopHeader = fn->GetLoopHeader(i);
  211. Js::EntryPointInfo * entryPointInfo = loopHeader->GetCurrentEntryPointInfo();
  212. this->GenerateLoopBody(fn, loopHeader, entryPointInfo);
  213. }
  214. }
  215. else
  216. {
  217. // A JIT attempt should have already been made through GenerateFunction
  218. Assert(!fn->GetDefaultFunctionEntryPointInfo()->IsNotScheduled());
  219. }
  220. }
  221. for (uint i = 0; i < fn->GetNestedCount(); i++)
  222. {
  223. Js::FunctionBody* functionToJIT = fn->GetNestedFunctionForExecution(i)->GetFunctionBody();
  224. GenerateAllFunctions(functionToJIT);
  225. }
  226. }
  227. #endif
  228. #if _M_ARM
  229. USHORT ArmExtractThumbImmediate16(PUSHORT address)
  230. {
  231. return ((address[0] << 12) & 0xf000) | // bits[15:12] in OP0[3:0]
  232. ((address[0] << 1) & 0x0800) | // bits[11] in OP0[10]
  233. ((address[1] >> 4) & 0x0700) | // bits[10:8] in OP1[14:12]
  234. ((address[1] >> 0) & 0x00ff); // bits[7:0] in OP1[7:0]
  235. }
  236. void ArmInsertThumbImmediate16(PUSHORT address, USHORT immediate)
  237. {
  238. USHORT opcode0;
  239. USHORT opcode1;
  240. opcode0 = address[0];
  241. opcode1 = address[1];
  242. opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1));
  243. opcode1 &= ~((0x0700 << 4) | (0x00ff << 0));
  244. opcode0 |= (immediate & 0xf000) >> 12; // bits[15:12] in OP0[3:0]
  245. opcode0 |= (immediate & 0x0800) >> 1; // bits[11] in OP0[10]
  246. opcode1 |= (immediate & 0x0700) << 4; // bits[10:8] in OP1[14:12]
  247. opcode1 |= (immediate & 0x00ff) << 0; // bits[7:0] in OP1[7:0]
  248. address[0] = opcode0;
  249. address[1] = opcode1;
  250. }
  251. #endif
  252. void DoFunctionRelocations(BYTE *function, DWORD functionOffset, DWORD functionSize, BYTE *module, size_t imageBase, IMAGE_SECTION_HEADER *textHeader, IMAGE_SECTION_HEADER *relocHeader)
  253. {
  254. PIMAGE_BASE_RELOCATION relocationBlock = (PIMAGE_BASE_RELOCATION)(module + relocHeader->PointerToRawData);
  255. for (; relocationBlock->VirtualAddress > 0 && ((BYTE *)relocationBlock < (module + relocHeader->PointerToRawData + relocHeader->SizeOfRawData)); )
  256. {
  257. DWORD blockOffset = relocationBlock->VirtualAddress - textHeader->VirtualAddress;
  258. // Skip relocation blocks that are before the function
  259. if ((blockOffset + 0x1000) > functionOffset)
  260. {
  261. unsigned short *relocation = (unsigned short *)((unsigned char *)relocationBlock + sizeof(IMAGE_BASE_RELOCATION));
  262. for (uint index = 0; index < ((relocationBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2); index++, relocation++)
  263. {
  264. int type = *relocation >> 12;
  265. int offset = *relocation & 0xfff;
  266. // If we are past the end of the function, we can stop.
  267. if ((blockOffset + offset) >= (functionOffset + functionSize))
  268. {
  269. break;
  270. }
  271. if ((blockOffset + offset) < functionOffset)
  272. {
  273. continue;
  274. }
  275. switch (type)
  276. {
  277. case IMAGE_REL_BASED_ABSOLUTE:
  278. break;
  279. #if _M_IX86
  280. case IMAGE_REL_BASED_HIGHLOW:
  281. {
  282. DWORD *patchAddrHL = (DWORD *) (function + blockOffset + offset - functionOffset);
  283. DWORD patchAddrHLOffset = *patchAddrHL - imageBase - textHeader->VirtualAddress;
  284. Assert((patchAddrHLOffset > functionOffset) && (patchAddrHLOffset < (functionOffset + functionSize)));
  285. *patchAddrHL = patchAddrHLOffset - functionOffset + (DWORD)function;
  286. }
  287. break;
  288. #elif defined(_M_X64_OR_ARM64)
  289. case IMAGE_REL_BASED_DIR64:
  290. {
  291. ULONGLONG *patchAddr64 = (ULONGLONG *) (function + blockOffset + offset - functionOffset);
  292. ULONGLONG patchAddr64Offset = *patchAddr64 - imageBase - textHeader->VirtualAddress;
  293. Assert((patchAddr64Offset > functionOffset) && (patchAddr64Offset < (functionOffset + functionSize)));
  294. *patchAddr64 = patchAddr64Offset - functionOffset + (ULONGLONG)function;
  295. }
  296. break;
  297. #else
  298. case IMAGE_REL_BASED_THUMB_MOV32:
  299. {
  300. USHORT *patchAddr = (USHORT *) (function + blockOffset + offset - functionOffset);
  301. DWORD address = ArmExtractThumbImmediate16(patchAddr) | (ArmExtractThumbImmediate16(patchAddr + 2) << 16);
  302. address = address - imageBase - textHeader->VirtualAddress - functionOffset + (DWORD)function;
  303. ArmInsertThumbImmediate16(patchAddr, (USHORT)(address & 0xFFFF));
  304. ArmInsertThumbImmediate16(patchAddr + 2, (USHORT)(address >> 16));
  305. }
  306. break;
  307. #endif
  308. default:
  309. Assert(false);
  310. break;
  311. }
  312. }
  313. }
  314. relocationBlock = (PIMAGE_BASE_RELOCATION) (((BYTE *) relocationBlock) + relocationBlock->SizeOfBlock);
  315. }
  316. }
  317. class AutoRestoreDefaultEntryPoint
  318. {
  319. public:
  320. AutoRestoreDefaultEntryPoint(Js::FunctionBody* functionBody):
  321. functionBody(functionBody)
  322. {
  323. this->oldDefaultEntryPoint = functionBody->GetDefaultFunctionEntryPointInfo();
  324. this->oldOriginalEntryPoint = functionBody->GetOriginalEntryPoint();
  325. this->newEntryPoint = functionBody->CreateNewDefaultEntryPoint();
  326. }
  327. ~AutoRestoreDefaultEntryPoint()
  328. {
  329. if (newEntryPoint && !newEntryPoint->IsCodeGenDone())
  330. {
  331. functionBody->RestoreOldDefaultEntryPoint(oldDefaultEntryPoint, oldOriginalEntryPoint, newEntryPoint);
  332. }
  333. }
  334. private:
  335. Js::FunctionBody* functionBody;
  336. Js::FunctionEntryPointInfo* oldDefaultEntryPoint;
  337. Js::JavascriptMethod oldOriginalEntryPoint;
  338. Js::FunctionEntryPointInfo* newEntryPoint;
  339. };
  340. //static
  341. void NativeCodeGenerator::Jit_TransitionFromSimpleJit(void *const framePointer)
  342. {
  343. TransitionFromSimpleJit(
  344. Js::ScriptFunction::FromVar(Js::JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject));
  345. }
  346. //static
  347. void NativeCodeGenerator::TransitionFromSimpleJit(Js::ScriptFunction *const function)
  348. {
  349. Assert(function);
  350. Js::FunctionBody *const functionBody = function->GetFunctionBody();
  351. Js::FunctionEntryPointInfo *const defaultEntryPointInfo = functionBody->GetDefaultFunctionEntryPointInfo();
  352. if(defaultEntryPointInfo == functionBody->GetSimpleJitEntryPointInfo())
  353. {
  354. Assert(functionBody->GetExecutionMode() == ExecutionMode::SimpleJit);
  355. Assert(function->GetFunctionEntryPointInfo() == defaultEntryPointInfo);
  356. // The latest entry point is the simple JIT, transition to the next execution mode and schedule a full JIT
  357. bool functionEntryPointUpdated = functionBody->GetScriptContext()->GetNativeCodeGenerator()->GenerateFunction(functionBody, function);
  358. if (functionEntryPointUpdated)
  359. {
  360. // Transition to the next execution mode after scheduling a full JIT, in case of OOM before the entry point is changed
  361. const bool transitioned = functionBody->TryTransitionToNextExecutionMode();
  362. Assert(transitioned);
  363. if (PHASE_TRACE(Js::SimpleJitPhase, functionBody))
  364. {
  365. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  366. Output::Print(
  367. _u("SimpleJit (TransitionFromSimpleJit): function: %s (%s)"),
  368. functionBody->GetDisplayName(),
  369. functionBody->GetDebugNumberSet(debugStringBuffer));
  370. Output::Flush();
  371. }
  372. }
  373. return;
  374. }
  375. if(function->GetFunctionEntryPointInfo() != defaultEntryPointInfo)
  376. {
  377. // A full JIT may have already been scheduled, or some entry point info got expired before the simple JIT entry point
  378. // was ready. In any case, the function's entry point info is not the latest, so update it.
  379. function->UpdateThunkEntryPoint(defaultEntryPointInfo, functionBody->GetDirectEntryPoint(defaultEntryPointInfo));
  380. }
  381. }
  382. #ifdef IR_VIEWER
  383. Js::Var
  384. NativeCodeGenerator::RejitIRViewerFunction(Js::FunctionBody *fn, Js::ScriptContext *requestContext)
  385. {
  386. /* Note: adapted from NativeCodeGenerator::GenerateFunction (NativeCodeGenerator.cpp) */
  387. Js::ScriptContext *scriptContext = fn->GetScriptContext();
  388. PageAllocator *pageAllocator = scriptContext->GetThreadContext()->GetPageAllocator();
  389. NativeCodeGenerator *nativeCodeGenerator = scriptContext->GetNativeCodeGenerator();
  390. AutoRestoreDefaultEntryPoint autoRestore(fn);
  391. Js::FunctionEntryPointInfo * entryPoint = fn->GetDefaultFunctionEntryPointInfo();
  392. JsFunctionCodeGen workitem(this, fn, entryPoint, fn->IsInDebugMode());
  393. workitem.isRejitIRViewerFunction = true;
  394. workitem.irViewerRequestContext = scriptContext;
  395. workitem.SetJitMode(ExecutionMode::FullJit);
  396. entryPoint->SetCodeGenPendingWithStackAllocatedWorkItem();
  397. entryPoint->SetCodeGenQueued();
  398. const auto recyclableData = GatherCodeGenData(fn, fn, entryPoint, &workitem);
  399. workitem.SetRecyclableData(recyclableData);
  400. nativeCodeGenerator->CodeGen(pageAllocator, &workitem, true);
  401. return Js::CrossSite::MarshalVar(requestContext, workitem.GetIRViewerOutput(scriptContext));
  402. }
  403. #endif /* IR_VIEWER */
  404. ///----------------------------------------------------------------------------
  405. ///
  406. /// NativeCodeGenerator::GenerateFunction
  407. ///
  408. /// This is the main entry point for the runtime to call the native code
  409. /// generator.
  410. ///
  411. ///----------------------------------------------------------------------------
  412. bool
  413. NativeCodeGenerator::GenerateFunction(Js::FunctionBody *fn, Js::ScriptFunction * function)
  414. {
  415. ASSERT_THREAD();
  416. Assert(!fn->GetIsFromNativeCodeModule());
  417. Assert(fn->GetScriptContext()->GetNativeCodeGenerator() == this);
  418. Assert(fn->GetFunctionBody() == fn);
  419. Assert(!fn->IsDeferred());
  420. #if !defined(_M_ARM64)
  421. if (fn->IsGeneratorAndJitIsDisabled())
  422. {
  423. // JITing generator functions is not complete nor stable yet so it is off by default.
  424. // Also try/catch JIT support in generator functions is not a goal for threshold
  425. // release so JITing generators containing try blocks is disabled for now.
  426. return false;
  427. }
  428. if (fn->IsInDebugMode() && fn->GetHasTry())
  429. {
  430. // Under debug mode disable JIT for functions that:
  431. // - have try
  432. return false;
  433. }
  434. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  435. if (Js::Configuration::Global.flags.Interpret &&
  436. fn->GetDisplayName() &&
  437. ::wcsstr(Js::Configuration::Global.flags.Interpret, fn->GetDisplayName()))
  438. {
  439. return false;
  440. }
  441. #endif
  442. if (fn->GetLoopCount() != 0 && fn->ForceJITLoopBody() && !fn->IsInDebugMode())
  443. {
  444. // Don't code gen the function if the function has loop, ForceJITLoopBody is on,
  445. // unless we are in debug mode in which case JIT loop body is disabled, even if it's forced.
  446. return false;
  447. }
  448. // Create a work item with null entry point- we'll set it once its allocated
  449. AutoPtr<JsFunctionCodeGen> workItemAutoPtr(this->NewFunctionCodeGen(fn, nullptr));
  450. if ((JsFunctionCodeGen*) workItemAutoPtr == nullptr)
  451. {
  452. // OOM, just skip this work item and return.
  453. return false;
  454. }
  455. Js::FunctionEntryPointInfo* entryPointInfo = nullptr;
  456. if (function != nullptr)
  457. {
  458. entryPointInfo = fn->CreateNewDefaultEntryPoint();
  459. }
  460. else
  461. {
  462. entryPointInfo = fn->GetDefaultFunctionEntryPointInfo();
  463. Assert(fn->IsInterpreterThunk() || fn->IsSimpleJitOriginalEntryPoint());
  464. }
  465. #ifdef ASMJS_PLAT
  466. if (fn->GetIsAsmjsMode())
  467. {
  468. AnalysisAssert(function != nullptr);
  469. Js::FunctionEntryPointInfo* oldFuncObjEntryPointInfo = (Js::FunctionEntryPointInfo*)function->GetEntryPointInfo();
  470. Assert(oldFuncObjEntryPointInfo->GetIsAsmJSFunction()); // should be asmjs entrypoint info
  471. // Set asmjs to be true in entrypoint
  472. entryPointInfo->SetIsAsmJSFunction(true);
  473. // Move the ModuleAddress from old Entrypoint to new entry point
  474. entryPointInfo->SetModuleAddress(oldFuncObjEntryPointInfo->GetModuleAddress());
  475. // Update the native address of the older entry point - this should be either the TJ entrypoint or the Interpreter Entry point
  476. entryPointInfo->SetNativeAddress(oldFuncObjEntryPointInfo->jsMethod);
  477. // have a reference to TJ entrypointInfo, this will be queued for collection in checkcodegen
  478. entryPointInfo->SetOldFunctionEntryPointInfo(oldFuncObjEntryPointInfo);
  479. Assert(PHASE_ON1(Js::AsmJsJITTemplatePhase) || (!oldFuncObjEntryPointInfo->GetIsTJMode() && !entryPointInfo->GetIsTJMode()));
  480. // this changes the address in the entrypointinfo to be the AsmJsCodgenThunk
  481. function->UpdateThunkEntryPoint(entryPointInfo, NativeCodeGenerator::CheckAsmJsCodeGenThunk);
  482. if (PHASE_TRACE1(Js::AsmjsEntryPointInfoPhase))
  483. Output::Print(_u("New Entrypoint is CheckAsmJsCodeGenThunk for function: %s\n"), fn->GetDisplayName());
  484. }
  485. else
  486. #endif
  487. {
  488. fn->SetCheckCodeGenEntryPoint(entryPointInfo, NativeCodeGenerator::CheckCodeGenThunk);
  489. if (function != nullptr)
  490. {
  491. function->UpdateThunkEntryPoint(entryPointInfo, NativeCodeGenerator::CheckCodeGenThunk);
  492. }
  493. }
  494. JsFunctionCodeGen * workitem = workItemAutoPtr.Detach();
  495. workitem->SetEntryPointInfo(entryPointInfo);
  496. entryPointInfo->SetCodeGenPending(workitem);
  497. InterlockedIncrement(&pendingCodeGenWorkItems);
  498. if(!IS_PREJIT_ON())
  499. {
  500. workItems.LinkToEnd(workitem);
  501. return true;
  502. }
  503. const ExecutionMode prejitJitMode = PrejitJitMode(fn);
  504. workitem->SetJitMode(prejitJitMode);
  505. try
  506. {
  507. AddToJitQueue(workitem, /*prioritize*/ true, /*lock*/ true, function);
  508. }
  509. catch (...)
  510. {
  511. // Add the item back to the list if AddToJitQueue throws. The position in the list is not important.
  512. workitem->ResetJitMode();
  513. workItems.LinkToEnd(workitem);
  514. throw;
  515. }
  516. fn->TraceExecutionMode("Prejit (before)");
  517. if(prejitJitMode == ExecutionMode::SimpleJit)
  518. {
  519. fn->TransitionToSimpleJitExecutionMode();
  520. }
  521. else
  522. {
  523. Assert(prejitJitMode == ExecutionMode::FullJit);
  524. fn->TransitionToFullJitExecutionMode();
  525. }
  526. fn->TraceExecutionMode("Prejit");
  527. Processor()->PrioritizeJobAndWait(this, entryPointInfo, function);
  528. CheckCodeGenDone(fn, entryPointInfo, function);
  529. return true;
  530. #else
  531. return false;
  532. #endif
  533. }
  534. void NativeCodeGenerator::GenerateLoopBody(Js::FunctionBody * fn, Js::LoopHeader * loopHeader, Js::EntryPointInfo* entryPoint, uint localCount, Js::Var localSlots[])
  535. {
  536. ASSERT_THREAD();
  537. Assert(fn->GetScriptContext()->GetNativeCodeGenerator() == this);
  538. Assert(entryPoint->jsMethod == nullptr);
  539. #if DBG_DUMP
  540. if (PHASE_TRACE1(Js::JITLoopBodyPhase))
  541. {
  542. fn->DumpFunctionId(true);
  543. Output::Print(_u(": %-20s LoopBody Start Loop: %2d ByteCode: %4d (%4d,%4d)\n"), fn->GetDisplayName(), fn->GetLoopNumber(loopHeader),
  544. loopHeader->endOffset - loopHeader->startOffset, loopHeader->startOffset, loopHeader->endOffset);
  545. Output::Flush();
  546. }
  547. #endif
  548. // If the parent function is JITted, no need to JIT this loop
  549. // CanReleaseLoopHeaders is a quick and dirty way of checking if the
  550. // function is currently being interpreted. If it is being interpreted,
  551. // We'd still like to jit the loop body.
  552. // We reset the interpretCount to 0 in case we switch back to the interpreter
  553. if (fn->GetNativeEntryPointUsed() && fn->GetCanReleaseLoopHeaders() && (!fn->GetIsAsmJsFunction() || !(loopHeader->GetCurrentEntryPointInfo()->GetIsTJMode())))
  554. {
  555. loopHeader->ResetInterpreterCount();
  556. return;
  557. }
  558. if (fn->GetIsAsmJsFunction())
  559. {
  560. Js::FunctionEntryPointInfo* functionEntryPointInfo = (Js::FunctionEntryPointInfo*) fn->GetDefaultEntryPointInfo();
  561. Js::LoopEntryPointInfo* loopEntryPointInfo = (Js::LoopEntryPointInfo*)entryPoint;
  562. loopEntryPointInfo->SetIsAsmJSFunction(true);
  563. loopEntryPointInfo->SetModuleAddress(functionEntryPointInfo->GetModuleAddress());
  564. }
  565. JsLoopBodyCodeGen * workitem = this->NewLoopBodyCodeGen(fn, entryPoint, loopHeader);
  566. if (!workitem)
  567. {
  568. // OOM, just skip this work item and return.
  569. return;
  570. }
  571. entryPoint->SetCodeGenPending(workitem);
  572. try
  573. {
  574. if (!fn->GetIsAsmJsFunction()) // not needed for asmjs as we don't profile in asm mode
  575. {
  576. const uint profiledRegBegin = fn->GetConstantCount();
  577. const uint profiledRegEnd = localCount;
  578. if (profiledRegBegin < profiledRegEnd)
  579. {
  580. workitem->GetJITData()->symIdToValueTypeMapCount = profiledRegEnd - profiledRegBegin;
  581. workitem->GetJITData()->symIdToValueTypeMap = (uint16*)HeapNewArrayZ(ValueType, workitem->GetJITData()->symIdToValueTypeMapCount);
  582. Recycler *recycler = fn->GetScriptContext()->GetRecycler();
  583. for (uint i = profiledRegBegin; i < profiledRegEnd; i++)
  584. {
  585. if (localSlots[i] && IsValidVar(localSlots[i], recycler))
  586. {
  587. workitem->GetJITData()->symIdToValueTypeMap[i - profiledRegBegin] = ValueType::Uninitialized.Merge(localSlots[i]).GetRawData();
  588. }
  589. }
  590. }
  591. }
  592. workitem->SetJitMode(ExecutionMode::FullJit);
  593. AddToJitQueue(workitem, /*prioritize*/ true, /*lock*/ true);
  594. }
  595. catch (...)
  596. {
  597. // If adding to the JIT queue fails we need to revert the state of the entry point
  598. // and delete the work item
  599. entryPoint->RevertToNotScheduled();
  600. workitem->Delete();
  601. throw;
  602. }
  603. if (!Processor()->ProcessesInBackground() || fn->ForceJITLoopBody())
  604. {
  605. Processor()->PrioritizeJobAndWait(this, entryPoint);
  606. }
  607. }
  608. bool
  609. NativeCodeGenerator::IsValidVar(const Js::Var var, Recycler *const recycler)
  610. {
  611. using namespace Js;
  612. Assert(var);
  613. Assert(recycler);
  614. // We may be handling uninitialized memory here, need to ensure that each recycler-allocated object is valid before it is
  615. // read. Virtual functions shouldn't be called because the type ID may match by coincidence but the vtable can still be
  616. // invalid, even if it is deemed to be a "valid" object, since that only validates that the memory is still owned by the
  617. // recycler. This function validates the memory that ValueType::Merge(Var) reads.
  618. if(TaggedInt::Is(var))
  619. {
  620. return true;
  621. }
  622. #if FLOATVAR
  623. if(JavascriptNumber::Is_NoTaggedIntCheck(var))
  624. {
  625. return true;
  626. }
  627. #endif
  628. RecyclableObject *const recyclableObject = RecyclableObject::FromVar(var);
  629. if(!recycler->IsValidObject(recyclableObject, sizeof(*recyclableObject)))
  630. {
  631. return false;
  632. }
  633. INT_PTR vtable = VirtualTableInfoBase::GetVirtualTable(var);
  634. if (vtable <= USHRT_MAX || (vtable & 1))
  635. {
  636. // Don't have a vtable, is it not a var, may be a frame display?
  637. return false;
  638. }
  639. Type *const type = recyclableObject->GetType();
  640. if(!recycler->IsValidObject(type, sizeof(*type)))
  641. {
  642. return false;
  643. }
  644. #if !FLOATVAR
  645. if(JavascriptNumber::Is_NoTaggedIntCheck(var))
  646. {
  647. return true;
  648. }
  649. #endif
  650. const TypeId typeId = type->GetTypeId();
  651. if(typeId < static_cast<TypeId>(0))
  652. {
  653. return false;
  654. }
  655. if(!DynamicType::Is(typeId))
  656. {
  657. return true;
  658. }
  659. DynamicType *const dynamicType = static_cast<DynamicType *>(type);
  660. if(!recycler->IsValidObject(dynamicType, sizeof(*dynamicType)))
  661. {
  662. return false;
  663. }
  664. DynamicTypeHandler *const typeHandler = dynamicType->GetTypeHandler();
  665. if(!recycler->IsValidObject(typeHandler, sizeof(*typeHandler)))
  666. {
  667. return false;
  668. }
  669. // Not using DynamicObject::FromVar since there's a virtual call in there
  670. DynamicObject *const object = static_cast<DynamicObject *>(recyclableObject);
  671. if(!recycler->IsValidObject(object, sizeof(*object)))
  672. {
  673. return false;
  674. }
  675. if(typeId != TypeIds_Array)
  676. {
  677. ArrayObject* const objectArray = object->GetObjectArrayUnchecked();
  678. return objectArray == nullptr || recycler->IsValidObject(objectArray, sizeof(*objectArray));
  679. }
  680. // Not using JavascriptArray::FromVar since there's a virtual call in there
  681. JavascriptArray *const array = static_cast<JavascriptArray *>(object);
  682. if(!recycler->IsValidObject(array, sizeof(*array)))
  683. {
  684. return false;
  685. }
  686. return true;
  687. }
  688. #if ENABLE_DEBUG_CONFIG_OPTIONS
  689. volatile UINT_PTR NativeCodeGenerator::CodegenFailureSeed = 0;
  690. #endif
  691. void
  692. NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* workItem, const bool foreground)
  693. {
  694. if(foreground)
  695. {
  696. // Func::Codegen has a lot of things on the stack, so probe the stack here instead
  697. PROBE_STACK(scriptContext, Js::Constants::MinStackJITCompile);
  698. }
  699. #if ENABLE_DEBUG_CONFIG_OPTIONS
  700. if (!foreground && Js::Configuration::Global.flags.IsEnabled(Js::InduceCodeGenFailureFlag))
  701. {
  702. if (NativeCodeGenerator::CodegenFailureSeed == 0)
  703. {
  704. // Initialize the seed
  705. NativeCodeGenerator::CodegenFailureSeed = Js::Configuration::Global.flags.InduceCodeGenFailureSeed;
  706. if (NativeCodeGenerator::CodegenFailureSeed == 0)
  707. {
  708. LARGE_INTEGER ctr;
  709. ::QueryPerformanceCounter(&ctr);
  710. NativeCodeGenerator::CodegenFailureSeed = ctr.HighPart ^ ctr.LowPart;
  711. srand((uint)NativeCodeGenerator::CodegenFailureSeed);
  712. }
  713. }
  714. int v = Math::Rand() % 100;
  715. if (v < Js::Configuration::Global.flags.InduceCodeGenFailure)
  716. {
  717. switch (v % 3)
  718. {
  719. case 0: Js::Throw::OutOfMemory(); break;
  720. case 1: throw Js::StackOverflowException(); break;
  721. case 2: throw Js::OperationAbortedException(); break;
  722. default:
  723. Assert(false);
  724. }
  725. }
  726. }
  727. #endif
  728. bool irviewerInstance = false;
  729. #ifdef IR_VIEWER
  730. irviewerInstance = true;
  731. #endif
  732. Assert(
  733. workItem->Type() != JsFunctionType ||
  734. irviewerInstance ||
  735. IsThunk(workItem->GetFunctionBody()->GetDirectEntryPoint(workItem->GetEntryPoint())) ||
  736. IsAsmJsCodeGenThunk(workItem->GetFunctionBody()->GetDirectEntryPoint(workItem->GetEntryPoint())));
  737. InterlockedExchangeAdd(&this->byteCodeSizeGenerated, workItem->GetByteCodeCount()); // must be interlocked because this data may be modified in the foreground and background thread concurrently
  738. Js::FunctionBody* body = workItem->GetFunctionBody();
  739. int nRegs = body->GetLocalsCount();
  740. AssertMsg((nRegs + 1) == (int)(SymID)(nRegs + 1), "SymID too small...");
  741. #ifdef ENABLE_BASIC_TELEMETRY
  742. double startTime = scriptContext->GetThreadContext()->JITTelemetry.Now();
  743. #endif
  744. if (body->GetScriptContext()->IsClosed())
  745. {
  746. // Should not be jitting something in the foreground when the script context is actually closed
  747. Assert(IsBackgroundJIT() || !body->GetScriptContext()->IsActuallyClosed());
  748. throw Js::OperationAbortedException();
  749. }
  750. workItem->GetJITData()->nativeDataAddr = (__int3264)workItem->GetEntryPoint()->GetNativeDataBufferRef();
  751. // TODO: oop jit can we be more efficient here?
  752. ArenaAllocator alloc(_u("JitData"), pageAllocator, Js::Throw::OutOfMemory);
  753. auto& jitData = workItem->GetJITData()->jitData;
  754. jitData = AnewStructZ(&alloc, FunctionJITTimeDataIDL);
  755. FunctionJITTimeInfo::BuildJITTimeData(&alloc, workItem->RecyclableData()->JitTimeData(), nullptr, workItem->GetJITData()->jitData, false, foreground);
  756. Js::EntryPointInfo * epInfo = workItem->GetEntryPoint();
  757. if (workItem->Type() == JsFunctionType)
  758. {
  759. auto funcEPInfo = (Js::FunctionEntryPointInfo*)epInfo;
  760. jitData->callsCountAddress = (uintptr_t)&funcEPInfo->callsCount;
  761. }
  762. else
  763. {
  764. workItem->GetJITData()->jittedLoopIterationsSinceLastBailoutAddr = (intptr_t)Js::FunctionBody::GetJittedLoopIterationsSinceLastBailoutAddress(epInfo);
  765. }
  766. jitData->sharedPropertyGuards = epInfo->GetSharedPropertyGuardsWithLock(&alloc, jitData->sharedPropGuardCount);
  767. JITOutputIDL jitWriteData = {0};
  768. #if !FLOATVAR
  769. workItem->GetJITData()->xProcNumberPageSegment = scriptContext->GetThreadContext()->GetXProcNumberPageSegmentManager()->GetFreeSegment(&alloc);
  770. #endif
  771. LARGE_INTEGER start_time = { 0 };
  772. NativeCodeGenerator::LogCodeGenStart(workItem, &start_time);
  773. workItem->GetJITData()->startTime = (int64)start_time.QuadPart;
  774. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  775. {
  776. workItem->codeGenResult = JITManager::GetJITManager()->RemoteCodeGenCall(
  777. workItem->GetJITData(),
  778. scriptContext->GetThreadContext()->GetRemoteThreadContextAddr(),
  779. scriptContext->GetRemoteScriptAddr(),
  780. &jitWriteData);
  781. switch (workItem->codeGenResult)
  782. {
  783. case S_OK:
  784. break;
  785. case E_ABORT:
  786. throw Js::OperationAbortedException();
  787. case E_OUTOFMEMORY:
  788. Js::Throw::OutOfMemory();
  789. case VBSERR_OutOfStack:
  790. throw Js::StackOverflowException();
  791. case E_ACCESSDENIED:
  792. // OOP JIT TODO: if server side can't handle request any more, use better error code and turn off JIT
  793. throw Js::JITOperationFailedException(workItem->codeGenResult);
  794. default:
  795. Js::Throw::FatalInternalError();
  796. }
  797. }
  798. else
  799. {
  800. CodeGenAllocators *const allocators =
  801. foreground ? EnsureForegroundAllocators(pageAllocator) : GetBackgroundAllocator(pageAllocator); // okay to do outside lock since the respective function is called only from one thread
  802. NoRecoverMemoryJitArenaAllocator jitArena(_u("JITArena"), pageAllocator, Js::Throw::OutOfMemory);
  803. JITTimeWorkItem * jitWorkItem = Anew(&jitArena, JITTimeWorkItem, workItem->GetJITData());
  804. #if !FLOATVAR
  805. CodeGenNumberAllocator* pNumberAllocator = nullptr;
  806. // the number allocator needs to be on the stack so that if we are doing foreground JIT
  807. // the chunk allocated from the recycler will be stacked pinned
  808. CodeGenNumberAllocator numberAllocator(
  809. foreground ? nullptr : scriptContext->GetThreadContext()->GetCodeGenNumberThreadAllocator(),
  810. scriptContext->GetRecycler());
  811. pNumberAllocator = &numberAllocator;
  812. #endif
  813. Js::ScriptContextProfiler *const codeGenProfiler =
  814. #ifdef PROFILE_EXEC
  815. foreground ? EnsureForegroundCodeGenProfiler() : GetBackgroundCodeGenProfiler(pageAllocator); // okay to do outside lock since the respective function is called only from one thread
  816. #else
  817. nullptr;
  818. #endif
  819. Func::Codegen(&jitArena, jitWorkItem, scriptContext->GetThreadContext(),
  820. scriptContext, &jitWriteData, epInfo, nullptr, jitWorkItem->GetPolymorphicInlineCacheInfo(), allocators,
  821. #if !FLOATVAR
  822. pNumberAllocator,
  823. #endif
  824. codeGenProfiler, !foreground);
  825. }
  826. if (JITManager::GetJITManager()->IsOOPJITEnabled() && PHASE_VERBOSE_TRACE(Js::BackEndPhase, workItem->GetFunctionBody()))
  827. {
  828. LARGE_INTEGER freq;
  829. LARGE_INTEGER end_time;
  830. QueryPerformanceCounter(&end_time);
  831. QueryPerformanceFrequency(&freq);
  832. Output::Print(
  833. _u("BackendMarshalOut - function: %s time:%8.6f mSec\r\n"),
  834. workItem->GetFunctionBody()->GetDisplayName(),
  835. (((double)((end_time.QuadPart - jitWriteData.startTime)* (double)1000.0 / (double)freq.QuadPart))) / (1));
  836. Output::Flush();
  837. }
  838. NativeCodeGenerator::LogCodeGenDone(workItem, &start_time);
  839. workItem->GetFunctionBody()->SetFrameHeight(workItem->GetEntryPoint(), jitWriteData.frameHeight);
  840. if (workItem->Type() == JsFunctionType)
  841. {
  842. Js::FunctionEntryPointInfo * funcEP = (Js::FunctionEntryPointInfo*)workItem->GetEntryPoint();
  843. funcEP->localVarSlotsOffset = jitWriteData.localVarSlotsOffset;
  844. funcEP->localVarChangedOffset = jitWriteData.localVarChangedOffset;
  845. }
  846. if (jitWriteData.hasJittedStackClosure != FALSE)
  847. {
  848. workItem->GetEntryPoint()->SetHasJittedStackClosure();
  849. }
  850. if (jitWriteData.numberPageSegments)
  851. {
  852. if (jitWriteData.numberPageSegments->pageAddress == 0)
  853. {
  854. midl_user_free(jitWriteData.numberPageSegments);
  855. jitWriteData.numberPageSegments = nullptr;
  856. }
  857. else
  858. {
  859. // TODO: when codegen fail, need to return the segment as well
  860. epInfo->SetNumberPageSegment(jitWriteData.numberPageSegments);
  861. }
  862. }
  863. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  864. {
  865. if (jitWriteData.nativeDataFixupTable)
  866. {
  867. for (unsigned int i = 0; i < jitWriteData.nativeDataFixupTable->count; i++)
  868. {
  869. auto& record = jitWriteData.nativeDataFixupTable->fixupRecords[i];
  870. auto updateList = record.updateList;
  871. if (PHASE_TRACE1(Js::NativeCodeDataPhase))
  872. {
  873. Output::Print(_u("NativeCodeData Fixup: allocIndex:%d, len:%x, totalOffset:%x, startAddress:%p\n"),
  874. record.index, record.length, record.startOffset, jitWriteData.buffer->data + record.startOffset);
  875. }
  876. while (updateList)
  877. {
  878. void* addrToFixup = jitWriteData.buffer->data + record.startOffset + updateList->addrOffset;
  879. void* targetAddr = jitWriteData.buffer->data + updateList->targetTotalOffset;
  880. if (PHASE_TRACE1(Js::NativeCodeDataPhase))
  881. {
  882. Output::Print(_u("\tEntry: +%x %p(%p) ==> %p\n"), updateList->addrOffset, addrToFixup, *(void**)(addrToFixup), targetAddr);
  883. }
  884. *(void**)(addrToFixup) = targetAddr;
  885. auto current = updateList;
  886. updateList = updateList->next;
  887. midl_user_free(current);
  888. }
  889. }
  890. midl_user_free(jitWriteData.nativeDataFixupTable);
  891. jitWriteData.nativeDataFixupTable = nullptr;
  892. // change the address with the fixup information
  893. *epInfo->GetNativeDataBufferRef() = (char*)jitWriteData.buffer->data;
  894. #if DBG
  895. if (PHASE_TRACE1(Js::NativeCodeDataPhase))
  896. {
  897. Output::Print(_u("NativeCodeData Client Buffer: %p, len: %x\n"), jitWriteData.buffer->data, jitWriteData.buffer->len);
  898. }
  899. #endif
  900. }
  901. epInfo->GetJitTransferData()->SetRuntimeTypeRefs(jitWriteData.pinnedTypeRefs);
  902. if (jitWriteData.throwMapCount > 0)
  903. {
  904. Js::ThrowMapEntry * throwMap = (Js::ThrowMapEntry *)(jitWriteData.buffer->data + jitWriteData.throwMapOffset);
  905. Js::SmallSpanSequenceIter iter;
  906. for (uint i = 0; i < jitWriteData.throwMapCount; ++i)
  907. {
  908. workItem->RecordNativeThrowMap(iter, throwMap[i].nativeBufferOffset, throwMap[i].statementIndex);
  909. }
  910. }
  911. }
  912. if (workItem->GetJitMode() != ExecutionMode::SimpleJit)
  913. {
  914. epInfo->RecordInlineeFrameOffsetsInfo(jitWriteData.inlineeFrameOffsetArrayOffset, jitWriteData.inlineeFrameOffsetArrayCount);
  915. epInfo->GetJitTransferData()->SetEquivalentTypeGuardOffsets(jitWriteData.equivalentTypeGuardOffsets);
  916. epInfo->GetJitTransferData()->SetTypeGuardTransferData(&jitWriteData);
  917. Assert(jitWriteData.ctorCacheEntries == nullptr || epInfo->GetConstructorCacheCount() > 0);
  918. epInfo->GetJitTransferData()->SetCtorCacheTransferData(&jitWriteData);
  919. workItem->GetEntryPoint()->GetJitTransferData()->SetIsReady();
  920. }
  921. #if defined(_M_X64)
  922. XDataAllocation * xdataInfo = HeapNewZ(XDataAllocation);
  923. xdataInfo->address = (byte*)jitWriteData.xdataAddr;
  924. XDataAllocator::Register(xdataInfo, jitWriteData.codeAddress, jitWriteData.codeSize);
  925. epInfo->SetXDataInfo(xdataInfo);
  926. #endif
  927. #if defined(_M_ARM)
  928. // for in-proc jit we do registration in encoder
  929. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  930. {
  931. XDataAllocation * xdataInfo = HeapNewZ(XDataAllocation);
  932. xdataInfo->pdataCount = jitWriteData.pdataCount;
  933. xdataInfo->xdataSize = jitWriteData.xdataSize;
  934. if (jitWriteData.buffer)
  935. {
  936. xdataInfo->address = jitWriteData.buffer->data + jitWriteData.xdataOffset;
  937. for (ushort i = 0; i < xdataInfo->pdataCount; ++i)
  938. {
  939. RUNTIME_FUNCTION *function = xdataInfo->GetPdataArray() + i;
  940. // if flag is 0, then we have separate .xdata, for which we need to fixup the address
  941. if (function->Flag == 0)
  942. {
  943. // UnwindData was set on server as the offset from the beginning of xdata buffer
  944. function->UnwindData = (DWORD)(xdataInfo->address + function->UnwindData);
  945. Assert(((DWORD)function->UnwindData & 0x3) == 0); // 4 byte aligned
  946. }
  947. }
  948. }
  949. else
  950. {
  951. xdataInfo->address = nullptr;
  952. }
  953. // unmask thumb mode from code address
  954. XDataAllocator::Register(xdataInfo, jitWriteData.codeAddress & ~0x1, jitWriteData.codeSize);
  955. epInfo->SetXDataInfo(xdataInfo);
  956. }
  957. #endif
  958. workItem->SetCodeAddress((size_t)jitWriteData.codeAddress);
  959. workItem->GetEntryPoint()->SetCodeGenRecorded((Js::JavascriptMethod)jitWriteData.codeAddress, jitWriteData.codeSize);
  960. if (jitWriteData.hasBailoutInstr != FALSE)
  961. {
  962. body->SetHasBailoutInstrInJittedCode(true);
  963. }
  964. if (!jitWriteData.isInPrereservedRegion)
  965. {
  966. scriptContext->GetThreadContext()->ResetIsAllJITCodeInPreReservedRegion();
  967. }
  968. body->m_argUsedForBranch |= jitWriteData.argUsedForBranch;
  969. if (body->HasDynamicProfileInfo())
  970. {
  971. if (jitWriteData.disableAggressiveIntTypeSpec)
  972. {
  973. body->GetAnyDynamicProfileInfo()->DisableAggressiveIntTypeSpec(workItem->Type() == JsLoopBodyWorkItemType);
  974. }
  975. if (jitWriteData.disableStackArgOpt)
  976. {
  977. body->GetAnyDynamicProfileInfo()->DisableStackArgOpt();
  978. }
  979. if (jitWriteData.disableSwitchOpt)
  980. {
  981. body->GetAnyDynamicProfileInfo()->DisableSwitchOpt();
  982. }
  983. if (jitWriteData.disableTrackCompoundedIntOverflow)
  984. {
  985. body->GetAnyDynamicProfileInfo()->DisableTrackCompoundedIntOverflow();
  986. }
  987. }
  988. if (jitWriteData.disableInlineApply)
  989. {
  990. body->SetDisableInlineApply(true);
  991. }
  992. if (jitWriteData.disableInlineSpread)
  993. {
  994. body->SetDisableInlineSpread(true);
  995. }
  996. #ifdef PROFILE_BAILOUT_RECORD_MEMORY
  997. if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory)
  998. {
  999. scriptContext->codeSize += workItem->GetEntryPoint()->GetCodeSize();
  1000. }
  1001. #endif
  1002. #ifdef ENABLE_BASIC_TELEMETRY
  1003. scriptContext->GetThreadContext()->JITTelemetry.LogTime(scriptContext->GetThreadContext()->JITTelemetry.Now() - startTime);
  1004. #endif
  1005. #ifdef BGJIT_STATS
  1006. // Must be interlocked because the following data may be modified from the background and foreground threads concurrently
  1007. Js::ScriptContext *scriptContext = workItem->GetScriptContext();
  1008. if (workItem->Type() == JsFunctionType)
  1009. {
  1010. InterlockedExchangeAdd(&scriptContext->bytecodeJITCount, workItem->GetByteCodeCount());
  1011. InterlockedIncrement(&scriptContext->funcJITCount);
  1012. }
  1013. else if(workItem->Type() == JsLoopBodyWorkItemType)
  1014. {
  1015. InterlockedIncrement(&scriptContext->loopJITCount);
  1016. }
  1017. #endif
  1018. }
  1019. /* static */
  1020. void NativeCodeGenerator::LogCodeGenStart(CodeGenWorkItem * workItem, LARGE_INTEGER * start_time)
  1021. {
  1022. Js::FunctionBody * body = workItem->GetFunctionBody();
  1023. {
  1024. if (IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_START()))
  1025. {
  1026. WCHAR displayNameBuffer[256];
  1027. WCHAR* displayName = displayNameBuffer;
  1028. size_t sizeInChars = workItem->GetDisplayName(displayName, 256);
  1029. if (sizeInChars > 256)
  1030. {
  1031. displayName = new WCHAR[sizeInChars];
  1032. workItem->GetDisplayName(displayName, 256);
  1033. }
  1034. JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_START(
  1035. body->GetFunctionNumber(),
  1036. displayName,
  1037. body->GetScriptContext(),
  1038. workItem->GetInterpretedCount(),
  1039. (const unsigned int)body->LengthInBytes(),
  1040. body->GetByteCodeCount(),
  1041. body->GetByteCodeInLoopCount(),
  1042. (int)workItem->GetJitMode()));
  1043. if (displayName != displayNameBuffer)
  1044. {
  1045. delete[] displayName;
  1046. }
  1047. }
  1048. }
  1049. #if DBG_DUMP
  1050. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::BackEndPhase))
  1051. {
  1052. if (workItem->GetEntryPoint()->IsLoopBody())
  1053. {
  1054. Output::Print(_u("---BeginBackEnd: function: %s, loop:%d---\r\n"), body->GetDisplayName(), ((JsLoopBodyCodeGen*)workItem)->GetLoopNumber());
  1055. }
  1056. else
  1057. {
  1058. Output::Print(_u("---BeginBackEnd: function: %s---\r\n"), body->GetDisplayName());
  1059. }
  1060. Output::Flush();
  1061. }
  1062. #endif
  1063. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1064. if (PHASE_TRACE(Js::BackEndPhase, body))
  1065. {
  1066. QueryPerformanceCounter(start_time);
  1067. if (workItem->GetEntryPoint()->IsLoopBody())
  1068. {
  1069. Output::Print(
  1070. _u("BeginBackEnd - function: %s (%s, line %u), loop: %u, mode: %S"),
  1071. body->GetDisplayName(),
  1072. body->GetDebugNumberSet(debugStringBuffer),
  1073. body->GetLineNumber(),
  1074. ((JsLoopBodyCodeGen*)workItem)->GetLoopNumber(),
  1075. ExecutionModeName(workItem->GetJitMode()));
  1076. if (body->GetIsAsmjsMode())
  1077. {
  1078. Output::Print(_u(" (Asmjs)\n"));
  1079. }
  1080. else
  1081. {
  1082. Output::Print(_u("\n"));
  1083. }
  1084. }
  1085. else
  1086. {
  1087. Output::Print(
  1088. _u("BeginBackEnd - function: %s (%s, line %u), mode: %S"),
  1089. body->GetDisplayName(),
  1090. body->GetDebugNumberSet(debugStringBuffer),
  1091. body->GetLineNumber(),
  1092. ExecutionModeName(workItem->GetJitMode()));
  1093. if (body->GetIsAsmjsMode())
  1094. {
  1095. Output::Print(_u(" (Asmjs)\n"));
  1096. }
  1097. else
  1098. {
  1099. Output::Print(_u("\n"));
  1100. }
  1101. }
  1102. Output::Flush();
  1103. }
  1104. #ifdef FIELD_ACCESS_STATS
  1105. if (PHASE_TRACE(Js::ObjTypeSpecPhase, body) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, body))
  1106. {
  1107. if (workItem->RecyclableData()->JitTimeData()->inlineCacheStats)
  1108. {
  1109. auto stats = workItem->RecyclableData()->JitTimeData()->inlineCacheStats;
  1110. Output::Print(_u("ObjTypeSpec: jitting function %s (#%s): inline cache stats:\n"), body->GetDisplayName(), body->GetDebugNumberSet(debugStringBuffer));
  1111. Output::Print(_u(" overall: total %u, no profile info %u\n"), stats->totalInlineCacheCount, stats->noInfoInlineCacheCount);
  1112. Output::Print(_u(" mono: total %u, empty %u, cloned %u\n"),
  1113. stats->monoInlineCacheCount, stats->emptyMonoInlineCacheCount, stats->clonedMonoInlineCacheCount);
  1114. Output::Print(_u(" poly: total %u (high %u, low %u), null %u, empty %u, ignored %u, disabled %u, equivalent %u, non-equivalent %u, cloned %u\n"),
  1115. stats->polyInlineCacheCount, stats->highUtilPolyInlineCacheCount, stats->lowUtilPolyInlineCacheCount,
  1116. stats->nullPolyInlineCacheCount, stats->emptyPolyInlineCacheCount, stats->ignoredPolyInlineCacheCount, stats->disabledPolyInlineCacheCount,
  1117. stats->equivPolyInlineCacheCount, stats->nonEquivPolyInlineCacheCount, stats->clonedPolyInlineCacheCount);
  1118. }
  1119. else
  1120. {
  1121. Output::Print(_u("EquivObjTypeSpec: function %s (%s): inline cache stats unavailable\n"), body->GetDisplayName(), body->GetDebugNumberSet(debugStringBuffer));
  1122. }
  1123. Output::Flush();
  1124. }
  1125. #endif
  1126. }
  1127. /* static */
  1128. void NativeCodeGenerator::LogCodeGenDone(CodeGenWorkItem * workItem, LARGE_INTEGER * start_time)
  1129. {
  1130. Js::FunctionBody * body = workItem->GetFunctionBody();
  1131. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1132. {
  1133. if (IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_STOP()))
  1134. {
  1135. WCHAR displayNameBuffer[256];
  1136. WCHAR* displayName = displayNameBuffer;
  1137. size_t sizeInChars = workItem->GetDisplayName(displayName, 256);
  1138. if (sizeInChars > 256)
  1139. {
  1140. displayName = new WCHAR[sizeInChars];
  1141. workItem->GetDisplayName(displayName, 256);
  1142. }
  1143. void* entryPoint;
  1144. ptrdiff_t codeSize;
  1145. workItem->GetEntryPointAddress(&entryPoint, &codeSize);
  1146. JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_STOP(
  1147. body->GetFunctionNumber(),
  1148. displayName,
  1149. body->GetScriptContext(),
  1150. workItem->GetInterpretedCount(),
  1151. entryPoint,
  1152. codeSize));
  1153. if (displayName != displayNameBuffer)
  1154. {
  1155. delete[] displayName;
  1156. }
  1157. }
  1158. }
  1159. #if DBG_DUMP
  1160. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::BackEndPhase))
  1161. {
  1162. Output::Print(_u("---EndBackEnd---\r\n"));
  1163. Output::Flush();
  1164. }
  1165. #endif
  1166. if (PHASE_TRACE(Js::BackEndPhase, body))
  1167. {
  1168. LARGE_INTEGER freq;
  1169. LARGE_INTEGER end_time;
  1170. QueryPerformanceCounter(&end_time);
  1171. QueryPerformanceFrequency(&freq);
  1172. if (workItem->GetEntryPoint()->IsLoopBody())
  1173. {
  1174. Output::Print(
  1175. _u("EndBackEnd - function: %s (%s, line %u), loop: %u, mode: %S, time:%8.6f mSec"),
  1176. body->GetDisplayName(),
  1177. body->GetDebugNumberSet(debugStringBuffer),
  1178. body->GetLineNumber(),
  1179. ((JsLoopBodyCodeGen*)workItem)->GetLoopNumber(),
  1180. ExecutionModeName(workItem->GetJitMode()),
  1181. (((double)((end_time.QuadPart - start_time->QuadPart)* (double)1000.0 / (double)freq.QuadPart))) / (1));
  1182. if (body->GetIsAsmjsMode())
  1183. {
  1184. Output::Print(_u(" (Asmjs)\n"));
  1185. }
  1186. else
  1187. {
  1188. Output::Print(_u("\n"));
  1189. }
  1190. }
  1191. else
  1192. {
  1193. Output::Print(
  1194. _u("EndBackEnd - function: %s (%s, line %u), mode: %S time:%8.6f mSec"),
  1195. body->GetDisplayName(),
  1196. body->GetDebugNumberSet(debugStringBuffer),
  1197. body->GetLineNumber(),
  1198. ExecutionModeName(workItem->GetJitMode()),
  1199. (((double)((end_time.QuadPart - start_time->QuadPart)* (double)1000.0 / (double)freq.QuadPart))) / (1));
  1200. if (body->GetIsAsmjsMode())
  1201. {
  1202. Output::Print(_u(" (Asmjs)\n"));
  1203. }
  1204. else
  1205. {
  1206. Output::Print(_u("\n"));
  1207. }
  1208. }
  1209. Output::Flush();
  1210. }
  1211. }
  1212. void NativeCodeGenerator::SetProfileMode(BOOL fSet)
  1213. {
  1214. this->SetNativeEntryPoint = fSet? Js::FunctionBody::ProfileSetNativeEntryPoint : Js::FunctionBody::DefaultSetNativeEntryPoint;
  1215. }
  1216. #if _M_IX86
  1217. __declspec(naked)
  1218. Js::Var
  1219. NativeCodeGenerator::CheckAsmJsCodeGenThunk(Js::RecyclableObject* function, Js::CallInfo callInfo, ...)
  1220. {
  1221. __asm
  1222. {
  1223. push ebp
  1224. mov ebp, esp
  1225. push function
  1226. call NativeCodeGenerator::CheckAsmJsCodeGen
  1227. #ifdef _CONTROL_FLOW_GUARD
  1228. // verify that the call target is valid
  1229. push eax
  1230. mov ecx, eax
  1231. call[__guard_check_icall_fptr]
  1232. pop eax
  1233. #endif
  1234. pop ebp
  1235. jmp eax
  1236. }
  1237. }
  1238. #elif _M_X64 || _M_ARM || _M_ARM64
  1239. // Do nothing: the implementation of NativeCodeGenerator::CheckCodeGenThunk is declared (appropriately decorated) in
  1240. // Backend\amd64\Thunks.asm and Backend\arm\Thunks.asm and Backend\arm64\Thunks.asm respectively.
  1241. #else
  1242. #error Not implemented.
  1243. #endif
  1244. #if _M_IX86
  1245. __declspec(naked)
  1246. Js::Var
  1247. NativeCodeGenerator::CheckCodeGenThunk(Js::RecyclableObject* function, Js::CallInfo callInfo, ...)
  1248. {
  1249. __asm
  1250. {
  1251. push ebp
  1252. mov ebp, esp
  1253. push [esp+8]
  1254. call NativeCodeGenerator::CheckCodeGen
  1255. #ifdef _CONTROL_FLOW_GUARD
  1256. // verify that the call target is valid
  1257. push eax
  1258. mov ecx, eax
  1259. call[__guard_check_icall_fptr]
  1260. pop eax
  1261. #endif
  1262. pop ebp
  1263. jmp eax
  1264. }
  1265. }
  1266. #elif _M_X64 || _M_ARM || _M_ARM64
  1267. // Do nothing: the implementation of NativeCodeGenerator::CheckCodeGenThunk is declared (appropriately decorated) in
  1268. // Backend\amd64\Thunks.asm and Backend\arm\Thunks.asm and Backend\arm64\Thunks.asm respectively.
  1269. #else
  1270. #error Not implemented.
  1271. #endif
  1272. bool
  1273. NativeCodeGenerator::IsThunk(Js::JavascriptMethod codeAddress)
  1274. {
  1275. return codeAddress == NativeCodeGenerator::CheckCodeGenThunk;
  1276. }
  1277. bool
  1278. NativeCodeGenerator::IsAsmJsCodeGenThunk(Js::JavascriptMethod codeAddress)
  1279. {
  1280. #ifdef ASMJS_PLAT
  1281. return codeAddress == NativeCodeGenerator::CheckAsmJsCodeGenThunk;
  1282. #else
  1283. return false;
  1284. #endif
  1285. }
  1286. CheckCodeGenFunction
  1287. NativeCodeGenerator::GetCheckCodeGenFunction(Js::JavascriptMethod codeAddress)
  1288. {
  1289. if (codeAddress == NativeCodeGenerator::CheckCodeGenThunk)
  1290. {
  1291. return NativeCodeGenerator::CheckCodeGen;
  1292. }
  1293. return nullptr;
  1294. }
  1295. Js::Var
  1296. NativeCodeGenerator::CheckAsmJsCodeGen(Js::ScriptFunction * function)
  1297. {
  1298. Assert(function);
  1299. Js::FunctionBody *functionBody = function->GetFunctionBody();
  1300. Js::ScriptContext *scriptContext = functionBody->GetScriptContext();
  1301. NativeCodeGenerator *nativeCodeGen = scriptContext->GetNativeCodeGenerator();
  1302. Assert(scriptContext->GetThreadContext()->IsScriptActive());
  1303. Assert(scriptContext->GetThreadContext()->IsInScript());
  1304. // Load the entry point here to validate it got changed afterwards
  1305. Js::FunctionEntryPointInfo* entryPoint = function->GetFunctionEntryPointInfo();
  1306. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1307. if ((PHASE_ON1(Js::AsmJsJITTemplatePhase) && CONFIG_FLAG(MaxTemplatizedJitRunCount) >= 0) || (!PHASE_ON1(Js::AsmJsJITTemplatePhase) && CONFIG_FLAG(MaxAsmJsInterpreterRunCount) >= 0))
  1308. {
  1309. nativeCodeGen->Processor()->PrioritizeJobAndWait(nativeCodeGen, entryPoint, function);
  1310. } else
  1311. #endif
  1312. if (!nativeCodeGen->Processor()->PrioritizeJob(nativeCodeGen, entryPoint, function))
  1313. {
  1314. if (PHASE_TRACE1(Js::AsmjsEntryPointInfoPhase))
  1315. {
  1316. Output::Print(_u("Codegen not done yet for function: %s, Entrypoint is CheckAsmJsCodeGenThunk\n"), function->GetFunctionBody()->GetDisplayName());
  1317. }
  1318. return reinterpret_cast<Js::Var>(entryPoint->GetNativeAddress());
  1319. }
  1320. if (PHASE_TRACE1(Js::AsmjsEntryPointInfoPhase))
  1321. {
  1322. Output::Print(_u("CodeGen Done for function: %s, Changing Entrypoint to Full JIT\n"), function->GetFunctionBody()->GetDisplayName());
  1323. }
  1324. // we will need to set the functionbody external and asmjs entrypoint to the fulljit entrypoint
  1325. return reinterpret_cast<Js::Var>(CheckCodeGenDone(functionBody, entryPoint, function));
  1326. }
  1327. Js::JavascriptMethod
  1328. NativeCodeGenerator::CheckCodeGen(Js::ScriptFunction * function)
  1329. {
  1330. Assert(function);
  1331. Assert(function->GetEntryPoint() == NativeCodeGenerator::CheckCodeGenThunk
  1332. || Js::CrossSite::IsThunk(function->GetEntryPoint()));
  1333. // We are not expecting non-deserialized functions here; Error if it hasn't been deserialized by this point
  1334. Js::FunctionBody *functionBody = function->GetFunctionBody();
  1335. Js::ScriptContext *scriptContext = functionBody->GetScriptContext();
  1336. NativeCodeGenerator *nativeCodeGen = scriptContext->GetNativeCodeGenerator();
  1337. Assert(scriptContext->GetThreadContext()->IsScriptActive());
  1338. Assert(scriptContext->GetThreadContext()->IsInScript());
  1339. // Load the entry point here to validate it got changed afterwards
  1340. Js::JavascriptMethod originalEntryPoint = functionBody->GetOriginalEntryPoint();
  1341. Js::FunctionEntryPointInfo* entryPoint = function->GetFunctionEntryPointInfo();
  1342. Js::FunctionEntryPointInfo *const defaultEntryPointInfo = functionBody->GetDefaultFunctionEntryPointInfo();
  1343. if(entryPoint != defaultEntryPointInfo)
  1344. {
  1345. // Switch to the latest entry point info
  1346. function->UpdateThunkEntryPoint(defaultEntryPointInfo, functionBody->GetDirectEntryPoint(defaultEntryPointInfo));
  1347. const Js::JavascriptMethod defaultDirectEntryPoint = functionBody->GetDirectEntryPoint(defaultEntryPointInfo);
  1348. if(!IsThunk(defaultDirectEntryPoint))
  1349. {
  1350. return defaultDirectEntryPoint;
  1351. }
  1352. entryPoint = defaultEntryPointInfo;
  1353. }
  1354. // If a transition to JIT needs to be forced, JIT right away
  1355. if(Js::Configuration::Global.flags.EnforceExecutionModeLimits &&
  1356. functionBody->GetExecutionMode() != ExecutionMode::SimpleJit &&
  1357. functionBody->TryTransitionToJitExecutionMode())
  1358. {
  1359. nativeCodeGen->Processor()->PrioritizeJobAndWait(nativeCodeGen, entryPoint, function);
  1360. return CheckCodeGenDone(functionBody, entryPoint, function);
  1361. }
  1362. if(!nativeCodeGen->Processor()->PrioritizeJob(nativeCodeGen, entryPoint, function))
  1363. {
  1364. #ifdef ENABLE_SCRIPT_PROFILING
  1365. #define originalEntryPoint_IS_ProfileDeferredParsingThunk \
  1366. (originalEntryPoint == ProfileDeferredParsingThunk)
  1367. #else
  1368. #define originalEntryPoint_IS_ProfileDeferredParsingThunk \
  1369. false
  1370. #endif
  1371. // Job was not yet processed
  1372. // originalEntryPoint is the last known good entry point for the function body. Here we verify that
  1373. // it either corresponds with this codegen episode (identified by function->entryPointIndex) of the function body
  1374. // or one that was scheduled after. In the latter case originalEntryPoint will get updated if and when
  1375. // that last episode completes successfully.
  1376. Assert(functionBody->GetDefaultEntryPointInfo() == function->GetEntryPointInfo() &&
  1377. (
  1378. originalEntryPoint == DefaultEntryThunk
  1379. || scriptContext->IsDynamicInterpreterThunk(originalEntryPoint)
  1380. || originalEntryPoint_IS_ProfileDeferredParsingThunk
  1381. || originalEntryPoint == DefaultDeferredParsingThunk
  1382. || (
  1383. functionBody->GetSimpleJitEntryPointInfo() &&
  1384. originalEntryPoint ==
  1385. reinterpret_cast<Js::JavascriptMethod>(functionBody->GetSimpleJitEntryPointInfo()->GetNativeAddress())
  1386. )
  1387. ) ||
  1388. functionBody->GetDefaultFunctionEntryPointInfo()->entryPointIndex > function->GetFunctionEntryPointInfo()->entryPointIndex);
  1389. return (scriptContext->CurrentThunk == ProfileEntryThunk) ? ProfileEntryThunk : originalEntryPoint;
  1390. }
  1391. return CheckCodeGenDone(functionBody, entryPoint, function);
  1392. }
  1393. Js::JavascriptMethod
  1394. NativeCodeGenerator::CheckCodeGenDone(
  1395. Js::FunctionBody *const functionBody,
  1396. Js::FunctionEntryPointInfo *const entryPointInfo,
  1397. Js::ScriptFunction * function)
  1398. {
  1399. Assert(!function || function->GetFunctionBody() == functionBody);
  1400. Assert(!function || function->GetFunctionEntryPointInfo() == entryPointInfo);
  1401. // Job was processed or failed and cleaned up
  1402. // We won't call CheckCodeGenDone if the job is still pending since
  1403. // PrioritizeJob will return false
  1404. Assert(entryPointInfo->IsCodeGenDone() || entryPointInfo->IsCleanedUp() || entryPointInfo->IsPendingCleanup());
  1405. if (!functionBody->GetHasBailoutInstrInJittedCode() && functionBody->GetHasAllocatedLoopHeaders() && (!functionBody->GetIsAsmJsFunction() || !(((Js::FunctionEntryPointInfo*)functionBody->GetDefaultEntryPointInfo())->GetIsTJMode())))
  1406. {
  1407. if (functionBody->GetCanReleaseLoopHeaders())
  1408. {
  1409. functionBody->ReleaseLoopHeaders();
  1410. }
  1411. else
  1412. {
  1413. functionBody->SetPendingLoopHeaderRelease(true);
  1414. }
  1415. }
  1416. Js::ScriptContext *scriptContext = functionBody->GetScriptContext();
  1417. if (!functionBody->GetNativeEntryPointUsed())
  1418. {
  1419. #ifdef BGJIT_STATS
  1420. scriptContext->jitCodeUsed += functionBody->GetByteCodeCount();
  1421. scriptContext->funcJitCodeUsed++;
  1422. #endif
  1423. functionBody->SetNativeEntryPointUsed(true);
  1424. }
  1425. // Replace the entry point
  1426. Js::JavascriptMethod jsMethod;
  1427. if (!entryPointInfo->IsCodeGenDone())
  1428. {
  1429. if (entryPointInfo->IsPendingCleanup())
  1430. {
  1431. entryPointInfo->Cleanup(false /* isShutdown */, true /* capture cleanup stack */);
  1432. }
  1433. jsMethod = functionBody->GetScriptContext()->CurrentThunk == ProfileEntryThunk ? ProfileEntryThunk : functionBody->GetOriginalEntryPoint();
  1434. entryPointInfo->jsMethod = jsMethod;
  1435. }
  1436. else
  1437. {
  1438. scriptContext->GetNativeCodeGenerator()->SetNativeEntryPoint(
  1439. entryPointInfo,
  1440. functionBody,
  1441. reinterpret_cast<Js::JavascriptMethod>(entryPointInfo->GetNativeAddress()));
  1442. jsMethod = entryPointInfo->jsMethod;
  1443. Assert(!functionBody->NeedEnsureDynamicProfileInfo() || jsMethod == Js::DynamicProfileInfo::EnsureDynamicProfileInfoThunk);
  1444. }
  1445. Assert(!IsThunk(jsMethod));
  1446. if(function)
  1447. {
  1448. function->UpdateThunkEntryPoint(entryPointInfo, jsMethod);
  1449. }
  1450. // call the direct entry point, which will ensure dynamic profile info if necessary
  1451. return jsMethod;
  1452. }
  1453. CodeGenWorkItem *
  1454. NativeCodeGenerator::GetJob(Js::EntryPointInfo * const entryPoint) const
  1455. {
  1456. ASSERT_THREAD();
  1457. Assert(entryPoint);
  1458. return entryPoint->GetWorkItem();
  1459. }
  1460. bool
  1461. NativeCodeGenerator::WasAddedToJobProcessor(JsUtil::Job *const job) const
  1462. {
  1463. // This function is called from inside the lock
  1464. ASSERT_THREAD();
  1465. Assert(job);
  1466. return static_cast<CodeGenWorkItem *>(job)->IsInJitQueue();
  1467. }
  1468. bool
  1469. NativeCodeGenerator::ShouldProcessInForeground(const bool willWaitForJob, const unsigned int numJobsInQueue) const
  1470. {
  1471. // This function is called from inside the lock
  1472. ASSERT_THREAD();
  1473. // Process the job synchronously in the foreground thread if we're waiting for the job to be processed, or if the background
  1474. // job queue is long enough and this native code generator is optimized for many instances (web workers)
  1475. return
  1476. willWaitForJob ||
  1477. (numJobsInQueue > (uint)CONFIG_FLAG(HybridFgJitBgQueueLengthThreshold) &&
  1478. (CONFIG_FLAG(HybridFgJit) || isOptimizedForManyInstances));
  1479. }
  1480. void
  1481. NativeCodeGenerator::PrioritizedButNotYetProcessed(JsUtil::Job *const job)
  1482. {
  1483. // This function is called from inside the lock
  1484. ASSERT_THREAD();
  1485. Assert(job);
  1486. #ifdef BGJIT_STATS
  1487. CodeGenWorkItem *const codeGenWorkItem = static_cast<CodeGenWorkItem *>(job);
  1488. if(codeGenWorkItem->Type() == JsFunctionType && codeGenWorkItem->IsInJitQueue())
  1489. {
  1490. codeGenWorkItem->GetScriptContext()->interpretedCallsHighPri++;
  1491. if(codeGenWorkItem->GetJitMode() == ExecutionMode::FullJit)
  1492. {
  1493. QueuedFullJitWorkItem *const queuedFullJitWorkItem = codeGenWorkItem->GetQueuedFullJitWorkItem();
  1494. if(queuedFullJitWorkItem)
  1495. {
  1496. queuedFullJitWorkItems.MoveToBeginning(queuedFullJitWorkItem);
  1497. }
  1498. }
  1499. }
  1500. #endif
  1501. }
  1502. void
  1503. NativeCodeGenerator::BeforeWaitForJob(Js::EntryPointInfo *const entryPoint) const
  1504. {
  1505. ASSERT_THREAD();
  1506. Assert(entryPoint);
  1507. #ifdef PROFILE_EXEC
  1508. ProfileBegin(this->foregroundCodeGenProfiler, Js::DelayPhase);
  1509. #endif
  1510. }
  1511. void
  1512. NativeCodeGenerator::AfterWaitForJob(Js::EntryPointInfo *const entryPoint) const
  1513. {
  1514. ASSERT_THREAD();
  1515. Assert(entryPoint);
  1516. #ifdef PROFILE_EXEC
  1517. ProfileEnd(this->foregroundCodeGenProfiler, Js::DelayPhase);
  1518. #endif
  1519. }
  1520. /*
  1521. * A workitem exceeds JIT limits if we've already generated MaxThreadJITCodeHeapSize
  1522. * (currently 7 MB) of code on this thread or MaxProcessJITCodeHeapSize (currently 55 MB)
  1523. * in the process. In real world websites we rarely (if at all) hit this limit.
  1524. * Also, if this workitem's byte code size is in excess of MaxJITFunctionBytecodeSize instructions,
  1525. * it exceeds the JIT limits
  1526. */
  1527. bool
  1528. NativeCodeGenerator::WorkItemExceedsJITLimits(CodeGenWorkItem *const codeGenWork)
  1529. {
  1530. return
  1531. (codeGenWork->GetScriptContext()->GetThreadContext()->GetCodeSize() >= Js::Constants::MaxThreadJITCodeHeapSize) ||
  1532. (ThreadContext::GetProcessCodeSize() >= Js::Constants::MaxProcessJITCodeHeapSize) ||
  1533. (codeGenWork->GetByteCodeCount() >= (uint)CONFIG_FLAG(MaxJITFunctionBytecodeSize));
  1534. }
  1535. bool
  1536. NativeCodeGenerator::Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData)
  1537. {
  1538. const bool foreground = !threadData;
  1539. PageAllocator *pageAllocator;
  1540. if (foreground)
  1541. {
  1542. pageAllocator = scriptContext->GetThreadContext()->GetPageAllocator();
  1543. }
  1544. else
  1545. {
  1546. pageAllocator = threadData->GetPageAllocator();
  1547. }
  1548. CodeGenWorkItem *const codeGenWork = static_cast<CodeGenWorkItem *>(job);
  1549. switch (codeGenWork->Type())
  1550. {
  1551. case JsLoopBodyWorkItemType:
  1552. {
  1553. JsLoopBodyCodeGen* loopBodyCodeGenWorkItem = (JsLoopBodyCodeGen*)codeGenWork;
  1554. Js::FunctionBody* fn = loopBodyCodeGenWorkItem->GetFunctionBody();
  1555. if (fn->GetNativeEntryPointUsed() && fn->GetCanReleaseLoopHeaders() && (!fn->GetIsAsmJsFunction() || !(loopBodyCodeGenWorkItem->loopHeader->GetCurrentEntryPointInfo()->GetIsTJMode())))
  1556. {
  1557. loopBodyCodeGenWorkItem->loopHeader->ResetInterpreterCount();
  1558. return false;
  1559. }
  1560. // Unless we're in a ForceNative configuration, ignore this workitem if it exceeds JIT limits
  1561. if (fn->ForceJITLoopBody() || !WorkItemExceedsJITLimits(codeGenWork))
  1562. {
  1563. CodeGen(pageAllocator, codeGenWork, foreground);
  1564. return true;
  1565. }
  1566. Js::EntryPointInfo * entryPoint = loopBodyCodeGenWorkItem->GetEntryPoint();
  1567. entryPoint->SetJITCapReached();
  1568. return false;
  1569. }
  1570. case JsFunctionType:
  1571. {
  1572. // Unless we're in a ForceNative configuration, ignore this workitem if it exceeds JIT limits
  1573. if (IS_PREJIT_ON() || Js::Configuration::Global.flags.ForceNative || !WorkItemExceedsJITLimits(codeGenWork))
  1574. {
  1575. CodeGen(pageAllocator, codeGenWork, foreground);
  1576. return true;
  1577. }
  1578. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1579. job->failureReason = Job::FailureReason::ExceedJITLimit;
  1580. #endif
  1581. return false;
  1582. }
  1583. default:
  1584. Assume(UNREACHED);
  1585. }
  1586. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1587. job->failureReason = Job::FailureReason::Unknown;
  1588. #endif
  1589. return false;
  1590. }
  1591. void
  1592. NativeCodeGenerator::Prioritize(JsUtil::Job *const job, const bool forceAddJobToProcessor, void* function)
  1593. {
  1594. // This function is called from inside the lock
  1595. ASSERT_THREAD();
  1596. Assert(job);
  1597. Assert(static_cast<const CodeGenWorkItem *>(job)->Type() == CodeGenWorkItemType::JsFunctionType);
  1598. Assert(!WasAddedToJobProcessor(job));
  1599. JsFunctionCodeGen *const workItem = static_cast<JsFunctionCodeGen *>(job);
  1600. Js::FunctionBody *const functionBody = workItem->GetFunctionBody();
  1601. Assert(workItem->GetEntryPoint() == functionBody->GetDefaultFunctionEntryPointInfo());
  1602. ExecutionMode jitMode;
  1603. if (functionBody->GetIsAsmjsMode())
  1604. {
  1605. jitMode = ExecutionMode::FullJit;
  1606. functionBody->SetExecutionMode(ExecutionMode::FullJit);
  1607. }
  1608. else
  1609. {
  1610. if(!forceAddJobToProcessor && !functionBody->TryTransitionToJitExecutionMode())
  1611. {
  1612. return;
  1613. }
  1614. jitMode = functionBody->GetExecutionMode();
  1615. Assert(jitMode == ExecutionMode::SimpleJit || jitMode == ExecutionMode::FullJit);
  1616. }
  1617. workItems.Unlink(workItem);
  1618. workItem->SetJitMode(jitMode);
  1619. try
  1620. {
  1621. // Prioritize full JIT work items over simple JIT work items. This simple solution seems sufficient for now, but it
  1622. // might be better to use a priority queue if it becomes necessary to prioritize recent simple JIT work items relative
  1623. // to the older simple JIT work items.
  1624. AddToJitQueue(
  1625. workItem,
  1626. jitMode == ExecutionMode::FullJit || queuedFullJitWorkItemCount == 0 /* prioritize */,
  1627. false /* lock */,
  1628. function);
  1629. }
  1630. catch (...)
  1631. {
  1632. // Add the item back to the list if AddToJitQueue throws. The position in the list is not important.
  1633. workItem->ResetJitMode();
  1634. workItems.LinkToEnd(workItem);
  1635. throw;
  1636. }
  1637. }
  1638. ExecutionMode NativeCodeGenerator::PrejitJitMode(Js::FunctionBody *const functionBody)
  1639. {
  1640. Assert(IS_PREJIT_ON());
  1641. Assert(functionBody->DoSimpleJit() || !PHASE_OFF(Js::FullJitPhase, functionBody));
  1642. // Prefer full JIT for prejitting unless it's off or simple JIT is forced
  1643. return
  1644. !PHASE_OFF(Js::FullJitPhase, functionBody) && !(PHASE_FORCE(Js::Phase::SimpleJitPhase, functionBody) && functionBody->DoSimpleJit())
  1645. ? ExecutionMode::FullJit
  1646. : ExecutionMode::SimpleJit;
  1647. }
  1648. void
  1649. NativeCodeGenerator::UpdateQueueForDebugMode()
  1650. {
  1651. Assert(!this->hasUpdatedQForDebugMode);
  1652. // If we're going to debug mode, drain the job processors queue of
  1653. // all jobs belonging this native code generator
  1654. // JobProcessed will be called for existing jobs, and in debug mode
  1655. // that method will simply add them back to the NativeCodeGen's queue
  1656. Processor()->RemoveManager(this);
  1657. this->hasUpdatedQForDebugMode = true;
  1658. if (Js::Configuration::Global.EnableJitInDebugMode())
  1659. {
  1660. Processor()->AddManager(this);
  1661. }
  1662. }
  1663. void
  1664. NativeCodeGenerator::JobProcessed(JsUtil::Job *const job, const bool succeeded)
  1665. {
  1666. // This function is called from inside the lock
  1667. Assert(job);
  1668. CodeGenWorkItem *workItem = static_cast<CodeGenWorkItem *>(job);
  1669. class AutoCleanup
  1670. {
  1671. private:
  1672. Js::ScriptContext *const scriptContext;
  1673. Js::CodeGenRecyclableData *const recyclableData;
  1674. public:
  1675. AutoCleanup(Js::ScriptContext *const scriptContext, Js::CodeGenRecyclableData *const recyclableData)
  1676. : scriptContext(scriptContext), recyclableData(recyclableData)
  1677. {
  1678. Assert(scriptContext);
  1679. }
  1680. ~AutoCleanup()
  1681. {
  1682. if(recyclableData)
  1683. {
  1684. scriptContext->GetThreadContext()->UnregisterCodeGenRecyclableData(recyclableData);
  1685. }
  1686. }
  1687. } autoCleanup(scriptContext, workItem->RecyclableData());
  1688. const ExecutionMode jitMode = workItem->GetJitMode();
  1689. if(jitMode == ExecutionMode::FullJit && workItem->IsInJitQueue())
  1690. {
  1691. QueuedFullJitWorkItem *const queuedFullJitWorkItem = workItem->GetQueuedFullJitWorkItem();
  1692. if(queuedFullJitWorkItem)
  1693. {
  1694. queuedFullJitWorkItems.Unlink(queuedFullJitWorkItem);
  1695. --queuedFullJitWorkItemCount;
  1696. }
  1697. }
  1698. Js::FunctionBody* functionBody = nullptr;
  1699. CodeGenWorkItemType workitemType = workItem->Type();
  1700. if (workitemType == JsFunctionType)
  1701. {
  1702. JsFunctionCodeGen * functionCodeGen = (JsFunctionCodeGen *)workItem;
  1703. functionBody = functionCodeGen->GetFunctionBody();
  1704. if (succeeded)
  1705. {
  1706. Js::FunctionEntryPointInfo* entryPointInfo = static_cast<Js::FunctionEntryPointInfo*>(functionCodeGen->GetEntryPoint());
  1707. entryPointInfo->SetJitMode(jitMode);
  1708. Assert(workItem->GetCodeAddress() != NULL);
  1709. entryPointInfo->SetCodeGenDone();
  1710. }
  1711. else
  1712. {
  1713. #if DBG
  1714. functionBody->m_nativeEntryPointIsInterpreterThunk = true;
  1715. #endif
  1716. // It's okay if the entry point has been reclaimed at this point
  1717. // since the job failed anyway so the entry point should never get used
  1718. // If it's still around, clean it up. If not, its finalizer would clean
  1719. // it up anyway.
  1720. Js::EntryPointInfo* entryPointInfo = functionCodeGen->GetEntryPoint();
  1721. if (entryPointInfo)
  1722. {
  1723. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1724. switch (job->failureReason)
  1725. {
  1726. case Job::FailureReason::OOM: entryPointInfo->SetCleanupReason(Js::EntryPointInfo::CleanupReason::CodeGenFailedOOM); break;
  1727. case Job::FailureReason::StackOverflow: entryPointInfo->SetCleanupReason(Js::EntryPointInfo::CleanupReason::CodeGenFailedStackOverflow); break;
  1728. case Job::FailureReason::Aborted: entryPointInfo->SetCleanupReason(Js::EntryPointInfo::CleanupReason::CodeGenFailedAborted); break;
  1729. case Job::FailureReason::ExceedJITLimit: entryPointInfo->SetCleanupReason(Js::EntryPointInfo::CleanupReason::CodeGenFailedExceedJITLimit); break;
  1730. case Job::FailureReason::Unknown: entryPointInfo->SetCleanupReason(Js::EntryPointInfo::CleanupReason::CodeGenFailedUnknown); break;
  1731. default: Assert(job->failureReason == Job::FailureReason::NotFailed);
  1732. }
  1733. #endif
  1734. entryPointInfo->SetPendingCleanup();
  1735. }
  1736. functionCodeGen->OnWorkItemProcessFail(this);
  1737. }
  1738. InterlockedDecrement(&pendingCodeGenWorkItems);
  1739. HeapDelete(functionCodeGen);
  1740. }
  1741. else if (workitemType == JsLoopBodyWorkItemType)
  1742. {
  1743. JsLoopBodyCodeGen * loopBodyCodeGen = (JsLoopBodyCodeGen*)workItem;
  1744. functionBody = loopBodyCodeGen->GetFunctionBody();
  1745. Js::EntryPointInfo * entryPoint = loopBodyCodeGen->GetEntryPoint();
  1746. if (succeeded)
  1747. {
  1748. Assert(workItem->GetCodeAddress() != NULL);
  1749. functionBody->SetLoopBodyEntryPoint(loopBodyCodeGen->loopHeader, loopBodyCodeGen->GetEntryPoint(), (Js::JavascriptMethod)workItem->GetCodeAddress());
  1750. entryPoint->SetCodeGenDone();
  1751. }
  1752. else
  1753. {
  1754. // We re-use failed loop body entry points.
  1755. // The loop body entry point could have been cleaned up if the parent function JITed,
  1756. // in which case we don't want to reset it.
  1757. if (entryPoint && !entryPoint->IsCleanedUp())
  1758. {
  1759. entryPoint->Reset(!entryPoint->IsJITCapReached()); // reset state to NotScheduled if JIT cap hasn't been reached
  1760. }
  1761. loopBodyCodeGen->OnWorkItemProcessFail(this);
  1762. }
  1763. HeapDelete(loopBodyCodeGen);
  1764. }
  1765. else
  1766. {
  1767. AssertMsg(false, "Unknown work item type");
  1768. AssertMsg(workItem->GetCodeAddress() == NULL, "No other types should have native entry point for now.");
  1769. }
  1770. }
  1771. void
  1772. NativeCodeGenerator::UpdateJITState()
  1773. {
  1774. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  1775. {
  1776. // ensure jit contexts have been set up
  1777. if (!scriptContext->GetRemoteScriptAddr())
  1778. {
  1779. scriptContext->InitializeRemoteScriptContext();
  1780. }
  1781. // update all property records on server that have been changed since last jit
  1782. ThreadContext::PropertyMap * pendingProps = scriptContext->GetThreadContext()->GetPendingJITProperties();
  1783. PropertyRecordIDL ** newPropArray = nullptr;
  1784. uint newCount = 0;
  1785. if (pendingProps->Count() > 0)
  1786. {
  1787. newCount = (uint)pendingProps->Count();
  1788. newPropArray = HeapNewArray(PropertyRecordIDL*, newCount);
  1789. uint index = 0;
  1790. auto iter = pendingProps->GetIteratorWithRemovalSupport();
  1791. while (iter.IsValid())
  1792. {
  1793. newPropArray[index++] = (PropertyRecordIDL*)iter.CurrentValue();
  1794. iter.RemoveCurrent();
  1795. iter.MoveNext();
  1796. }
  1797. Assert(index == newCount);
  1798. }
  1799. ThreadContext::PropertyList * reclaimedProps = scriptContext->GetThreadContext()->GetReclaimedJITProperties();
  1800. int * reclaimedPropArray = nullptr;
  1801. uint reclaimedCount = 0;
  1802. if (reclaimedProps->Count() > 0)
  1803. {
  1804. reclaimedCount = (uint)reclaimedProps->Count();
  1805. reclaimedPropArray = HeapNewArray(int, reclaimedCount);
  1806. uint index = 0;
  1807. while (!reclaimedProps->Empty())
  1808. {
  1809. reclaimedPropArray[index++] = (int)reclaimedProps->Pop();
  1810. }
  1811. Assert(index == reclaimedCount);
  1812. }
  1813. if (newCount > 0 || reclaimedCount > 0)
  1814. {
  1815. UpdatedPropertysIDL props = {0};
  1816. props.reclaimedPropertyCount = reclaimedCount;
  1817. props.reclaimedPropertyIdArray = reclaimedPropArray;
  1818. props.newRecordCount = newCount;
  1819. props.newRecordArray = newPropArray;
  1820. HRESULT hr = JITManager::GetJITManager()->UpdatePropertyRecordMap(scriptContext->GetThreadContext()->GetRemoteThreadContextAddr(), &props);
  1821. if (newPropArray)
  1822. {
  1823. HeapDeleteArray(newCount, newPropArray);
  1824. }
  1825. if (reclaimedPropArray)
  1826. {
  1827. HeapDeleteArray(reclaimedCount, reclaimedPropArray);
  1828. }
  1829. if (hr != S_OK)
  1830. {
  1831. // OOP JIT TODO: use better exception when failed to update JIT server state
  1832. Js::Throw::OutOfMemory();
  1833. }
  1834. }
  1835. }
  1836. }
  1837. JsUtil::Job *
  1838. NativeCodeGenerator::GetJobToProcessProactively()
  1839. {
  1840. ASSERT_THREAD();
  1841. // Look for work, starting with high priority items first, and above LowPri
  1842. CodeGenWorkItem* workItem = workItems.Head();
  1843. while(workItem != nullptr)
  1844. {
  1845. if(workItem->ShouldSpeculativelyJit(this->byteCodeSizeGenerated))
  1846. {
  1847. workItem->SetJitMode(ExecutionMode::FullJit);
  1848. // Note: This gives a perf regression in fre build, but it is useful for debugging and won't be there for the final build
  1849. // anyway, so I left it in.
  1850. if (PHASE_TRACE(Js::DelayPhase, workItem->GetFunctionBody())) {
  1851. OUTPUT_TRACE(Js::DelayPhase, _u("ScriptContext: 0x%p, Speculative JIT: %-25s, Byte code generated: %d \n"),
  1852. this->scriptContext, workItem->GetFunctionBody()->GetExternalDisplayName(), this->byteCodeSizeGenerated);
  1853. }
  1854. Js::FunctionBody *fn = workItem->GetFunctionBody();
  1855. Js::EntryPointInfo *entryPoint = workItem->GetEntryPoint();
  1856. const auto recyclableData = GatherCodeGenData(fn, fn, entryPoint, workItem);
  1857. workItems.Unlink(workItem);
  1858. workItem->SetRecyclableData(recyclableData);
  1859. {
  1860. AutoOptionalCriticalSection lock(Processor()->GetCriticalSection());
  1861. scriptContext->GetThreadContext()->RegisterCodeGenRecyclableData(recyclableData);
  1862. }
  1863. #ifdef BGJIT_STATS
  1864. scriptContext->speculativeJitCount++;
  1865. #endif
  1866. QueuedFullJitWorkItem *const queuedFullJitWorkItem = workItem->EnsureQueuedFullJitWorkItem();
  1867. if(queuedFullJitWorkItem) // ignore OOM, this work item just won't be removed from the job processor's queue
  1868. {
  1869. queuedFullJitWorkItems.LinkToBeginning(queuedFullJitWorkItem);
  1870. ++queuedFullJitWorkItemCount;
  1871. }
  1872. workItem->OnAddToJitQueue();
  1873. workItem->GetFunctionBody()->TraceExecutionMode("SpeculativeJit (before)");
  1874. workItem->GetFunctionBody()->TransitionToFullJitExecutionMode();
  1875. workItem->GetFunctionBody()->TraceExecutionMode("SpeculativeJit");
  1876. break;
  1877. }
  1878. workItem = static_cast<CodeGenWorkItem*>(workItem->Next());
  1879. }
  1880. return workItem;
  1881. }
  1882. // Removes all of the proactive jobs from the generator. Used when switching between attached/detached
  1883. // debug modes in order to drain the queue of jobs (since we switch from interpreted to native and back).
  1884. void
  1885. NativeCodeGenerator::RemoveProactiveJobs()
  1886. {
  1887. CodeGenWorkItem* workItem = workItems.Head();
  1888. while (workItem)
  1889. {
  1890. CodeGenWorkItem* temp = static_cast<CodeGenWorkItem*>(workItem->Next());
  1891. workItem->Delete();
  1892. workItem = temp;
  1893. }
  1894. workItems.Clear();
  1895. //for(JsUtil::Job *job = workItems.Head(); job;)
  1896. //{
  1897. // JsUtil::Job *const next = job->Next();
  1898. // JobProcessed(job, /*succeeded*/ false);
  1899. // job = next;
  1900. //}
  1901. }
  1902. template<bool IsInlinee>
  1903. void
  1904. NativeCodeGenerator::GatherCodeGenData(
  1905. Recycler *const recycler,
  1906. Js::FunctionBody *const topFunctionBody,
  1907. Js::FunctionBody *const functionBody,
  1908. Js::EntryPointInfo *const entryPoint,
  1909. InliningDecider &inliningDecider,
  1910. ObjTypeSpecFldInfoList *objTypeSpecFldInfoList,
  1911. Js::FunctionCodeGenJitTimeData *const jitTimeData,
  1912. Js::FunctionCodeGenRuntimeData *const runtimeData,
  1913. Js::JavascriptFunction* function,
  1914. bool isJitTimeDataComputed,
  1915. uint32 recursiveInlineDepth)
  1916. {
  1917. ASSERT_THREAD();
  1918. Assert(recycler);
  1919. Assert(functionBody);
  1920. Assert(jitTimeData);
  1921. Assert(IsInlinee == !!runtimeData);
  1922. Assert(!IsInlinee || (!inliningDecider.GetIsLoopBody() || !PHASE_OFF(Js::InlineInJitLoopBodyPhase, topFunctionBody)));
  1923. Assert(topFunctionBody != nullptr && (!entryPoint->GetWorkItem() || entryPoint->GetWorkItem()->GetFunctionBody() == topFunctionBody));
  1924. Assert(objTypeSpecFldInfoList != nullptr);
  1925. #ifdef FIELD_ACCESS_STATS
  1926. jitTimeData->EnsureInlineCacheStats(recycler);
  1927. #define SetInlineCacheCount(counter, value) jitTimeData->inlineCacheStats->counter = value;
  1928. #define IncInlineCacheCount(counter) if(!isJitTimeDataComputed) {jitTimeData->inlineCacheStats->counter++;}
  1929. #define AddInlineCacheStats(callerData, inlineeData) callerData->AddInlineeInlineCacheStats(inlineeData);
  1930. #define InlineCacheStatsArg(jitTimeData) !isJitTimeDataComputed ? jitTimeData->inlineCacheStats : nullptr
  1931. #else
  1932. #define SetInlineCacheCount(counter, value)
  1933. #define IncInlineCacheCount(counter)
  1934. #define AddInlineCacheStats(callerData, inlineeData)
  1935. #define InlineCacheStatsArg(jitTimeData) nullptr
  1936. #endif
  1937. #if DBG
  1938. Assert(
  1939. PHASE_ON(Js::Phase::SimulatePolyCacheWithOneTypeForFunctionPhase, functionBody) ==
  1940. CONFIG_ISENABLED(Js::Flag::SimulatePolyCacheWithOneTypeForInlineCacheIndexFlag));
  1941. if(PHASE_ON(Js::Phase::SimulatePolyCacheWithOneTypeForFunctionPhase, functionBody))
  1942. {
  1943. const Js::InlineCacheIndex inlineCacheIndex = CONFIG_FLAG(SimulatePolyCacheWithOneTypeForInlineCacheIndex);
  1944. functionBody->CreateNewPolymorphicInlineCache(
  1945. inlineCacheIndex,
  1946. functionBody->GetPropertyIdFromCacheId(inlineCacheIndex),
  1947. functionBody->GetInlineCache(inlineCacheIndex));
  1948. if(functionBody->HasDynamicProfileInfo())
  1949. {
  1950. functionBody->GetAnyDynamicProfileInfo()->RecordPolymorphicFieldAccess(functionBody, inlineCacheIndex);
  1951. }
  1952. }
  1953. #endif
  1954. if(IsInlinee)
  1955. {
  1956. // This function is recursive
  1957. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  1958. }
  1959. else
  1960. {
  1961. //TryAggressiveInlining adjusts inlining heuristics and walks the call tree. If it can inlining everything it will set the InliningThreshold to be aggressive.
  1962. if (!inliningDecider.GetIsLoopBody())
  1963. {
  1964. uint32 inlineeCount = 0;
  1965. if (!PHASE_OFF(Js::TryAggressiveInliningPhase, topFunctionBody))
  1966. {
  1967. Assert(topFunctionBody == functionBody);
  1968. inliningDecider.SetAggressiveHeuristics();
  1969. if (!TryAggressiveInlining(topFunctionBody, functionBody, inliningDecider, inlineeCount, 0))
  1970. {
  1971. uint countOfInlineesWithLoops = inliningDecider.GetNumberOfInlineesWithLoop();
  1972. //TryAggressiveInlining failed, set back to default heuristics.
  1973. inliningDecider.ResetInlineHeuristics();
  1974. inliningDecider.SetLimitOnInlineesWithLoop(countOfInlineesWithLoops);
  1975. }
  1976. else
  1977. {
  1978. jitTimeData->SetIsAggressiveInliningEnabled();
  1979. }
  1980. inliningDecider.ResetState();
  1981. }
  1982. }
  1983. entryPoint->EnsurePolymorphicInlineCacheInfo(recycler, functionBody);
  1984. }
  1985. entryPoint->EnsureJitTransferData(recycler);
  1986. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1987. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1988. #endif
  1989. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1990. if (PHASE_VERBOSE_TRACE(Js::ObjTypeSpecPhase, topFunctionBody) || PHASE_VERBOSE_TRACE(Js::EquivObjTypeSpecPhase, topFunctionBody))
  1991. {
  1992. Output::Print(_u("ObjTypeSpec: top function %s (%s), function %s (%s): GatherCodeGenData(): \n"),
  1993. topFunctionBody->GetDisplayName(), topFunctionBody->GetDebugNumberSet(debugStringBuffer), functionBody->GetDisplayName(), functionBody->GetDebugNumberSet(debugStringBuffer));
  1994. Output::Flush();
  1995. }
  1996. #endif
  1997. const auto profileData =
  1998. functionBody->HasDynamicProfileInfo()
  1999. ? functionBody->GetAnyDynamicProfileInfo()
  2000. : functionBody->EnsureDynamicProfileInfo();
  2001. bool inlineGetterSetter = false;
  2002. bool inlineApplyTarget = false; //to indicate whether we can inline apply target or not.
  2003. bool inlineCallTarget = false;
  2004. if(profileData)
  2005. {
  2006. if (!IsInlinee)
  2007. {
  2008. PHASE_PRINT_TRACE(
  2009. Js::ObjTypeSpecPhase, functionBody,
  2010. _u("Objtypespec (%s): Pending cache state on add %x to JIT queue: %d\n"),
  2011. functionBody->GetDebugNumberSet(debugStringBuffer), entryPoint, profileData->GetPolymorphicCacheState());
  2012. entryPoint->SetPendingPolymorphicCacheState(profileData->GetPolymorphicCacheState());
  2013. entryPoint->SetPendingInlinerVersion(profileData->GetInlinerVersion());
  2014. entryPoint->SetPendingImplicitCallFlags(profileData->GetImplicitCallFlags());
  2015. }
  2016. if (functionBody->GetProfiledArrayCallSiteCount() != 0)
  2017. {
  2018. RecyclerWeakReference<Js::FunctionBody> *weakFuncRef = recycler->CreateWeakReferenceHandle(functionBody);
  2019. if (!isJitTimeDataComputed)
  2020. {
  2021. jitTimeData->SetWeakFuncRef(weakFuncRef);
  2022. }
  2023. entryPoint->AddWeakFuncRef(weakFuncRef, recycler);
  2024. }
  2025. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  2026. if (PHASE_VERBOSE_TESTTRACE(Js::ObjTypeSpecPhase, functionBody) ||
  2027. PHASE_VERBOSE_TRACE1(Js::PolymorphicInlineCachePhase))
  2028. {
  2029. if (functionBody->GetInlineCacheCount() > 0)
  2030. {
  2031. if (!IsInlinee)
  2032. {
  2033. Output::Print(_u("-----------------------------------------------------------------------------\n"));
  2034. }
  2035. else
  2036. {
  2037. Output::Print(_u("\tInlinee:\t"));
  2038. }
  2039. functionBody->DumpFullFunctionName();
  2040. Output::Print(_u("\n"));
  2041. }
  2042. }
  2043. #endif
  2044. SetInlineCacheCount(totalInlineCacheCount, functionBody->GetInlineCacheCount());
  2045. Assert(functionBody->GetProfiledFldCount() == functionBody->GetInlineCacheCount()); // otherwise, isInst inline caches need to be cloned
  2046. for(uint i = 0; i < functionBody->GetInlineCacheCount(); ++i)
  2047. {
  2048. const auto cacheType = profileData->GetFldInfo(functionBody, i)->flags;
  2049. PHASE_PRINT_VERBOSE_TESTTRACE(
  2050. Js::ObjTypeSpecPhase, functionBody,
  2051. _u("Cache #%3d, Layout: %s, Profile info: %s\n"),
  2052. i,
  2053. functionBody->GetInlineCache(i)->LayoutString(),
  2054. cacheType == Js::FldInfo_NoInfo ? _u("none") :
  2055. (cacheType & Js::FldInfo_Polymorphic) ? _u("polymorphic") : _u("monomorphic"));
  2056. if (cacheType == Js::FldInfo_NoInfo)
  2057. {
  2058. IncInlineCacheCount(noInfoInlineCacheCount);
  2059. continue;
  2060. }
  2061. Js::PolymorphicInlineCache * polymorphicCacheOnFunctionBody = functionBody->GetPolymorphicInlineCache(i);
  2062. bool isPolymorphic = (cacheType & Js::FldInfo_Polymorphic) != 0;
  2063. if (!isPolymorphic)
  2064. {
  2065. Js::InlineCache *inlineCache;
  2066. if(function && Js::ScriptFunctionWithInlineCache::Is(function))
  2067. {
  2068. inlineCache = Js::ScriptFunctionWithInlineCache::FromVar(function)->GetInlineCache(i);
  2069. }
  2070. else
  2071. {
  2072. inlineCache = functionBody->GetInlineCache(i);
  2073. }
  2074. Js::ObjTypeSpecFldInfo* objTypeSpecFldInfo = nullptr;
  2075. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2076. if (PHASE_VERBOSE_TRACE(Js::ObjTypeSpecPhase, topFunctionBody) || PHASE_VERBOSE_TRACE(Js::EquivObjTypeSpecPhase, topFunctionBody))
  2077. {
  2078. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2079. Js::PropertyId propertyId = functionBody->GetPropertyIdFromCacheId(i);
  2080. Js::PropertyRecord const * const propertyRecord = functionBody->GetScriptContext()->GetPropertyName(propertyId);
  2081. Output::Print(_u("ObTypeSpec: top function %s (%s), function %s (%s): cloning mono cache for %s (#%d) cache %d \n"),
  2082. topFunctionBody->GetDisplayName(), topFunctionBody->GetDebugNumberSet(debugStringBuffer),
  2083. functionBody->GetDisplayName(), functionBody->GetDebugNumberSet(debugStringBuffer2), propertyRecord->GetBuffer(), propertyId, i);
  2084. Output::Flush();
  2085. }
  2086. #endif
  2087. IncInlineCacheCount(monoInlineCacheCount);
  2088. if (inlineCache->IsEmpty())
  2089. {
  2090. IncInlineCacheCount(emptyMonoInlineCacheCount);
  2091. }
  2092. if(!PHASE_OFF(Js::ObjTypeSpecPhase, functionBody) || !PHASE_OFF(Js::FixedMethodsPhase, functionBody) || !PHASE_OFF(Js::UseFixedDataPropsPhase, functionBody))
  2093. {
  2094. if(cacheType & (Js::FldInfo_FromLocal | Js::FldInfo_FromLocalWithoutProperty | Js::FldInfo_FromProto))
  2095. {
  2096. // WinBlue 170722: Disable ObjTypeSpec optimization for activation object in debug mode,
  2097. // as it can result in BailOutFailedTypeCheck before locals are set to undefined,
  2098. // which can result in using garbage object during bailout/restore values.
  2099. if (!(functionBody->IsInDebugMode() && inlineCache->GetType() &&
  2100. inlineCache->GetType()->GetTypeId() == Js::TypeIds_ActivationObject))
  2101. {
  2102. objTypeSpecFldInfo = Js::ObjTypeSpecFldInfo::CreateFrom(objTypeSpecFldInfoList->Count(), inlineCache, i, entryPoint, topFunctionBody, functionBody, InlineCacheStatsArg(jitTimeData));
  2103. if (objTypeSpecFldInfo)
  2104. {
  2105. IncInlineCacheCount(clonedMonoInlineCacheCount);
  2106. if (!PHASE_OFF(Js::InlineApplyTargetPhase, functionBody) && (cacheType & Js::FldInfo_InlineCandidate))
  2107. {
  2108. if (IsInlinee || objTypeSpecFldInfo->IsBuiltin())
  2109. {
  2110. inlineApplyTarget = true;
  2111. }
  2112. }
  2113. if (!PHASE_OFF(Js::InlineCallTargetPhase, functionBody) && (cacheType & Js::FldInfo_InlineCandidate))
  2114. {
  2115. inlineCallTarget = true;
  2116. }
  2117. if (!isJitTimeDataComputed)
  2118. {
  2119. jitTimeData->GetObjTypeSpecFldInfoArray()->SetInfo(recycler, functionBody, i, objTypeSpecFldInfo);
  2120. objTypeSpecFldInfoList->Prepend(objTypeSpecFldInfo);
  2121. }
  2122. }
  2123. }
  2124. }
  2125. }
  2126. if(!PHASE_OFF(Js::FixAccessorPropsPhase, functionBody))
  2127. {
  2128. if (!objTypeSpecFldInfo && (cacheType & Js::FldInfo_FromAccessor) && (cacheType & Js::FldInfo_InlineCandidate))
  2129. {
  2130. objTypeSpecFldInfo = Js::ObjTypeSpecFldInfo::CreateFrom(objTypeSpecFldInfoList->Count(), inlineCache, i, entryPoint, topFunctionBody, functionBody, InlineCacheStatsArg(jitTimeData));
  2131. if (objTypeSpecFldInfo)
  2132. {
  2133. inlineGetterSetter = true;
  2134. if (!isJitTimeDataComputed)
  2135. {
  2136. IncInlineCacheCount(clonedMonoInlineCacheCount);
  2137. jitTimeData->GetObjTypeSpecFldInfoArray()->SetInfo(recycler, functionBody, i, objTypeSpecFldInfo);
  2138. objTypeSpecFldInfoList->Prepend(objTypeSpecFldInfo);
  2139. }
  2140. }
  2141. }
  2142. }
  2143. if (!PHASE_OFF(Js::RootObjectFldFastPathPhase, functionBody))
  2144. {
  2145. if (i >= functionBody->GetRootObjectLoadInlineCacheStart() && inlineCache->IsLocal())
  2146. {
  2147. void * rawType = inlineCache->u.local.type;
  2148. Js::Type * type = TypeWithoutAuxSlotTag(rawType);
  2149. Js::RootObjectBase * rootObject = functionBody->GetRootObject();
  2150. if (rootObject->GetType() == type)
  2151. {
  2152. Js::BigPropertyIndex propertyIndex = inlineCache->u.local.slotIndex;
  2153. if (rawType == type)
  2154. {
  2155. // type is not tagged, inline slot
  2156. propertyIndex = rootObject->GetPropertyIndexFromInlineSlotIndex(inlineCache->u.local.slotIndex);
  2157. }
  2158. else
  2159. {
  2160. propertyIndex = rootObject->GetPropertyIndexFromAuxSlotIndex(inlineCache->u.local.slotIndex);
  2161. }
  2162. Js::PropertyAttributes attributes;
  2163. if (rootObject->GetAttributesWithPropertyIndex(functionBody->GetPropertyIdFromCacheId(i), propertyIndex, &attributes)
  2164. && (attributes & PropertyConfigurable) == 0
  2165. && !isJitTimeDataComputed)
  2166. {
  2167. // non configurable
  2168. if (objTypeSpecFldInfo == nullptr)
  2169. {
  2170. objTypeSpecFldInfo = Js::ObjTypeSpecFldInfo::CreateFrom(objTypeSpecFldInfoList->Count(), inlineCache, i, entryPoint, topFunctionBody, functionBody, InlineCacheStatsArg(jitTimeData));
  2171. if (objTypeSpecFldInfo)
  2172. {
  2173. IncInlineCacheCount(clonedMonoInlineCacheCount);
  2174. jitTimeData->GetObjTypeSpecFldInfoArray()->SetInfo(recycler, functionBody, i, objTypeSpecFldInfo);
  2175. objTypeSpecFldInfoList->Prepend(objTypeSpecFldInfo);
  2176. }
  2177. }
  2178. if (objTypeSpecFldInfo != nullptr)
  2179. {
  2180. objTypeSpecFldInfo->SetRootObjectNonConfigurableField(i < functionBody->GetRootObjectStoreInlineCacheStart());
  2181. }
  2182. }
  2183. }
  2184. }
  2185. }
  2186. }
  2187. // Even if the FldInfo says that the field access may be polymorphic, be optimistic that if the function object has inline caches, they'll be monomorphic
  2188. else if(function && Js::ScriptFunctionWithInlineCache::Is(function) && (cacheType & Js::FldInfo_InlineCandidate || !polymorphicCacheOnFunctionBody))
  2189. {
  2190. Js::InlineCache *inlineCache = Js::ScriptFunctionWithInlineCache::FromVar(function)->GetInlineCache(i);
  2191. Js::ObjTypeSpecFldInfo* objTypeSpecFldInfo = nullptr;
  2192. if(!PHASE_OFF(Js::ObjTypeSpecPhase, functionBody) || !PHASE_OFF(Js::FixedMethodsPhase, functionBody))
  2193. {
  2194. if(cacheType & (Js::FldInfo_FromLocal | Js::FldInfo_FromProto)) // Remove FldInfo_FromLocal?
  2195. {
  2196. // WinBlue 170722: Disable ObjTypeSpec optimization for activation object in debug mode,
  2197. // as it can result in BailOutFailedTypeCheck before locals are set to undefined,
  2198. // which can result in using garbage object during bailout/restore values.
  2199. if (!(functionBody->IsInDebugMode() && inlineCache->GetType() &&
  2200. inlineCache->GetType()->GetTypeId() == Js::TypeIds_ActivationObject))
  2201. {
  2202. objTypeSpecFldInfo = Js::ObjTypeSpecFldInfo::CreateFrom(objTypeSpecFldInfoList->Count(), inlineCache, i, entryPoint, topFunctionBody, functionBody, InlineCacheStatsArg(jitTimeData));
  2203. if (objTypeSpecFldInfo)
  2204. {
  2205. IncInlineCacheCount(clonedMonoInlineCacheCount);
  2206. if (!PHASE_OFF(Js::InlineApplyTargetPhase, functionBody) && IsInlinee && (cacheType & Js::FldInfo_InlineCandidate))
  2207. {
  2208. inlineApplyTarget = true;
  2209. }
  2210. if (!isJitTimeDataComputed)
  2211. {
  2212. jitTimeData->GetObjTypeSpecFldInfoArray()->SetInfo(recycler, functionBody, i, objTypeSpecFldInfo);
  2213. objTypeSpecFldInfoList->Prepend(objTypeSpecFldInfo);
  2214. }
  2215. }
  2216. }
  2217. }
  2218. }
  2219. }
  2220. else
  2221. {
  2222. const auto polymorphicInlineCache = functionBody->GetPolymorphicInlineCache(i);
  2223. if (polymorphicInlineCache != nullptr)
  2224. {
  2225. IncInlineCacheCount(polyInlineCacheCount);
  2226. if (profileData->GetFldInfo(functionBody, i)->ShouldUsePolymorphicInlineCache())
  2227. {
  2228. IncInlineCacheCount(highUtilPolyInlineCacheCount);
  2229. }
  2230. else
  2231. {
  2232. IncInlineCacheCount(lowUtilPolyInlineCacheCount);
  2233. }
  2234. if (!PHASE_OFF(Js::EquivObjTypeSpecPhase, topFunctionBody) && !topFunctionBody->GetAnyDynamicProfileInfo()->IsEquivalentObjTypeSpecDisabled())
  2235. {
  2236. if (!polymorphicInlineCache->GetIgnoreForEquivalentObjTypeSpec() || (polymorphicInlineCache->GetCloneForJitTimeUse() && !PHASE_OFF(Js::PolymorphicInlinePhase, functionBody) && !PHASE_OFF(Js::PolymorphicInlineFixedMethodsPhase, functionBody)))
  2237. {
  2238. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2239. if (PHASE_VERBOSE_TRACE(Js::ObjTypeSpecPhase, topFunctionBody) || PHASE_VERBOSE_TRACE(Js::EquivObjTypeSpecPhase, topFunctionBody))
  2240. {
  2241. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2242. Js::PropertyId propertyId = functionBody->GetPropertyIdFromCacheId(i);
  2243. Js::PropertyRecord const * const propertyRecord = functionBody->GetScriptContext()->GetPropertyName(propertyId);
  2244. Output::Print(_u("ObTypeSpec: top function %s (%s), function %s (%s): cloning poly cache for %s (#%d) cache %d \n"),
  2245. topFunctionBody->GetDisplayName(), topFunctionBody->GetDebugNumberSet(debugStringBuffer),
  2246. functionBody->GetDisplayName(), functionBody->GetDebugNumberSet(debugStringBuffer2), propertyRecord->GetBuffer(), propertyId, i);
  2247. Output::Flush();
  2248. }
  2249. #endif
  2250. Js::ObjTypeSpecFldInfo* objTypeSpecFldInfo = Js::ObjTypeSpecFldInfo::CreateFrom(objTypeSpecFldInfoList->Count(), polymorphicInlineCache, i, entryPoint, topFunctionBody, functionBody, InlineCacheStatsArg(jitTimeData));
  2251. if (objTypeSpecFldInfo != nullptr)
  2252. {
  2253. if (!isJitTimeDataComputed)
  2254. {
  2255. jitTimeData->GetObjTypeSpecFldInfoArray()->SetInfo(recycler, functionBody, i, objTypeSpecFldInfo);
  2256. IncInlineCacheCount(clonedPolyInlineCacheCount);
  2257. objTypeSpecFldInfoList->Prepend(objTypeSpecFldInfo);
  2258. }
  2259. if (!PHASE_OFF(Js::InlineAccessorsPhase, functionBody) && (cacheType & Js::FldInfo_FromAccessor) && (cacheType & Js::FldInfo_InlineCandidate))
  2260. {
  2261. inlineGetterSetter = true;
  2262. }
  2263. }
  2264. }
  2265. else
  2266. {
  2267. IncInlineCacheCount(ignoredPolyInlineCacheCount);
  2268. }
  2269. }
  2270. else
  2271. {
  2272. IncInlineCacheCount(disabledPolyInlineCacheCount);
  2273. }
  2274. }
  2275. else
  2276. {
  2277. IncInlineCacheCount(nullPolyInlineCacheCount);
  2278. }
  2279. if (polymorphicInlineCache != nullptr)
  2280. {
  2281. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2282. if (PHASE_VERBOSE_TRACE1(Js::PolymorphicInlineCachePhase))
  2283. {
  2284. if (IsInlinee) Output::Print(_u("\t"));
  2285. Output::Print(_u("\t%d: PIC size = %d\n"), i, polymorphicInlineCache->GetSize());
  2286. #if DBG_DUMP
  2287. polymorphicInlineCache->Dump();
  2288. #endif
  2289. }
  2290. else if (PHASE_TRACE1(Js::PolymorphicInlineCachePhase))
  2291. {
  2292. Js::PropertyId propertyId = functionBody->GetPropertyIdFromCacheId(i);
  2293. Js::PropertyRecord const * const propertyRecord = functionBody->GetScriptContext()->GetPropertyName(propertyId);
  2294. Output::Print(_u("Trace PIC JIT function %s (%s) field: %s (index: %d) \n"), functionBody->GetDisplayName(), functionBody->GetDebugNumberSet(debugStringBuffer),
  2295. propertyRecord->GetBuffer(), i);
  2296. }
  2297. #endif
  2298. byte polyCacheUtil = profileData->GetFldInfo(functionBody, i)->polymorphicInlineCacheUtilization;
  2299. entryPoint->GetPolymorphicInlineCacheInfo()->SetPolymorphicInlineCache(functionBody, i, polymorphicInlineCache, IsInlinee, polyCacheUtil);
  2300. if (IsInlinee)
  2301. {
  2302. Assert(entryPoint->GetPolymorphicInlineCacheInfo()->GetInlineeInfo(functionBody)->GetPolymorphicInlineCaches()->GetInlineCache(functionBody, i) == polymorphicInlineCache);
  2303. }
  2304. else
  2305. {
  2306. Assert(entryPoint->GetPolymorphicInlineCacheInfo()->GetSelfInfo()->GetPolymorphicInlineCaches()->GetInlineCache(functionBody, i) == polymorphicInlineCache);
  2307. }
  2308. }
  2309. else if(IsInlinee && CONFIG_FLAG(CloneInlinedPolymorphicCaches))
  2310. {
  2311. // Clone polymorphic inline caches for runtime usage in this inlinee. The JIT should only use the pointers to
  2312. // the inline caches, as their cached data is not guaranteed to be stable while jitting.
  2313. Js::InlineCache *const inlineCache =
  2314. function && Js::ScriptFunctionWithInlineCache::Is(function)
  2315. ? Js::ScriptFunctionWithInlineCache::FromVar(function)->GetInlineCache(i)
  2316. : functionBody->GetInlineCache(i);
  2317. Js::PropertyId propertyId = functionBody->GetPropertyIdFromCacheId(i);
  2318. const auto clone = runtimeData->ClonedInlineCaches()->GetInlineCache(functionBody, i);
  2319. if (clone)
  2320. {
  2321. inlineCache->CopyTo(propertyId, functionBody->GetScriptContext(), clone);
  2322. }
  2323. else
  2324. {
  2325. runtimeData->ClonedInlineCaches()->SetInlineCache(
  2326. recycler,
  2327. functionBody,
  2328. i,
  2329. inlineCache->Clone(propertyId, functionBody->GetScriptContext()));
  2330. }
  2331. }
  2332. }
  2333. }
  2334. }
  2335. // Gather code gen data for inlinees
  2336. if(IsInlinee ? !inliningDecider.InlineIntoInliner(functionBody) : !inliningDecider.InlineIntoTopFunc())
  2337. {
  2338. return;
  2339. }
  2340. class AutoCleanup
  2341. {
  2342. private:
  2343. Js::FunctionBody *const functionBody;
  2344. public:
  2345. AutoCleanup(Js::FunctionBody *const functionBody) : functionBody(functionBody)
  2346. {
  2347. functionBody->OnBeginInlineInto();
  2348. }
  2349. ~AutoCleanup()
  2350. {
  2351. functionBody->OnEndInlineInto();
  2352. }
  2353. } autoCleanup(functionBody);
  2354. const auto profiledCallSiteCount = functionBody->GetProfiledCallSiteCount();
  2355. Assert(profiledCallSiteCount != 0 || functionBody->GetAnyDynamicProfileInfo()->HasLdFldCallSiteInfo());
  2356. if (profiledCallSiteCount && !isJitTimeDataComputed)
  2357. {
  2358. jitTimeData->inlineesBv = BVFixed::New<Recycler>(profiledCallSiteCount, recycler);
  2359. }
  2360. // Iterate through profiled call sites recursively and determine what should be inlined
  2361. for(Js::ProfileId profiledCallSiteId = 0; profiledCallSiteId < profiledCallSiteCount; ++profiledCallSiteId)
  2362. {
  2363. Js::FunctionInfo *const inlinee = inliningDecider.InlineCallSite(functionBody, profiledCallSiteId, recursiveInlineDepth);
  2364. if(!inlinee)
  2365. {
  2366. if (profileData->CallSiteHasProfileData(profiledCallSiteId))
  2367. {
  2368. jitTimeData->inlineesBv->Set(profiledCallSiteId);
  2369. }
  2370. //Try and see if this polymorphic call
  2371. Js::FunctionBody* inlineeFunctionBodyArray[Js::DynamicProfileInfo::maxPolymorphicInliningSize] = {0};
  2372. bool canInlineArray[Js::DynamicProfileInfo::maxPolymorphicInliningSize] = { 0 };
  2373. uint polyInlineeCount = inliningDecider.InlinePolymorphicCallSite(functionBody, profiledCallSiteId, inlineeFunctionBodyArray,
  2374. Js::DynamicProfileInfo::maxPolymorphicInliningSize, canInlineArray);
  2375. //We should be able to inline at least two functions here.
  2376. if (polyInlineeCount >= 2)
  2377. {
  2378. for (uint id = 0; id < polyInlineeCount; id++)
  2379. {
  2380. bool isInlined = canInlineArray[id];
  2381. Js::FunctionCodeGenRuntimeData *inlineeRunTimeData = IsInlinee ? runtimeData->EnsureInlinee(recycler, profiledCallSiteId, inlineeFunctionBodyArray[id]) : functionBody->EnsureInlineeCodeGenRuntimeData(recycler, profiledCallSiteId, inlineeFunctionBodyArray[id]);
  2382. if (!isJitTimeDataComputed)
  2383. {
  2384. Js::FunctionCodeGenJitTimeData *inlineeJitTimeData = jitTimeData->AddInlinee(recycler, profiledCallSiteId, inlineeFunctionBodyArray[id], isInlined);
  2385. if (isInlined)
  2386. {
  2387. GatherCodeGenData<true>(
  2388. recycler,
  2389. topFunctionBody,
  2390. inlineeFunctionBodyArray[id],
  2391. entryPoint,
  2392. inliningDecider,
  2393. objTypeSpecFldInfoList,
  2394. inlineeJitTimeData,
  2395. inlineeRunTimeData
  2396. );
  2397. AddInlineCacheStats(jitTimeData, inlineeJitTimeData);
  2398. }
  2399. }
  2400. }
  2401. }
  2402. }
  2403. else
  2404. {
  2405. jitTimeData->inlineesBv->Set(profiledCallSiteId);
  2406. Js::FunctionBody *const inlineeFunctionBody = inlinee->GetFunctionBody();
  2407. if(!inlineeFunctionBody )
  2408. {
  2409. if (!isJitTimeDataComputed)
  2410. {
  2411. jitTimeData->AddInlinee(recycler, profiledCallSiteId, inlinee);
  2412. }
  2413. continue;
  2414. }
  2415. // We are at a callsite that can be inlined. Let the callsite be foo().
  2416. // If foo has inline caches on it, we need to be able to get those for cloning.
  2417. // To do this,
  2418. // 1. Retrieve the inline cache associated with the load of "foo",
  2419. // 2. Try to get the fixed function object corresponding to "foo",
  2420. // 3. Pass the fixed function object to GatherCodeGenData which can clone its inline caches.
  2421. uint ldFldInlineCacheIndex = profileData->GetLdFldCacheIndexFromCallSiteInfo(functionBody, profiledCallSiteId);
  2422. Js::InlineCache * inlineCache = nullptr;
  2423. if ((ldFldInlineCacheIndex != Js::Constants::NoInlineCacheIndex) && (ldFldInlineCacheIndex < functionBody->GetInlineCacheCount()))
  2424. {
  2425. if(function && Js::ScriptFunctionWithInlineCache::Is(function))
  2426. {
  2427. inlineCache = Js::ScriptFunctionWithInlineCache::FromVar(function)->GetInlineCache(ldFldInlineCacheIndex);
  2428. }
  2429. else
  2430. {
  2431. inlineCache = functionBody->GetInlineCache(ldFldInlineCacheIndex);
  2432. }
  2433. }
  2434. Js::JavascriptFunction* fixedFunctionObject = nullptr;
  2435. if (inlineCache && (inlineCache->IsLocal() || inlineCache->IsProto()))
  2436. {
  2437. inlineCache->TryGetFixedMethodFromCache(functionBody, ldFldInlineCacheIndex, &fixedFunctionObject);
  2438. }
  2439. if (fixedFunctionObject && !fixedFunctionObject->GetFunctionInfo()->IsDeferred() && fixedFunctionObject->GetFunctionBody() != inlineeFunctionBody)
  2440. {
  2441. fixedFunctionObject = nullptr;
  2442. }
  2443. if (!PHASE_OFF(Js::InlineRecursivePhase, functionBody))
  2444. {
  2445. if (!isJitTimeDataComputed)
  2446. {
  2447. Js::FunctionCodeGenRuntimeData *inlineeRuntimeData = IsInlinee ? runtimeData->EnsureInlinee(recycler, profiledCallSiteId, inlineeFunctionBody) : functionBody->EnsureInlineeCodeGenRuntimeData(recycler, profiledCallSiteId, inlineeFunctionBody);
  2448. Js::FunctionCodeGenJitTimeData *inlineeJitTimeData = nullptr;
  2449. bool doShareJitTimeData = false;
  2450. // Share the jitTime data if i) it is a recursive call, ii) jitTimeData is not from a polymorphic chain, and iii) all the call sites are recursive
  2451. if (functionBody == inlineeFunctionBody // recursive call
  2452. && jitTimeData->GetNext() == nullptr // not from a polymorphic call site
  2453. && profiledCallSiteCount == functionBody->GetNumberOfRecursiveCallSites() && !inlineGetterSetter) // all the callsites are recursive
  2454. {
  2455. jitTimeData->SetupRecursiveInlineeChain(recycler, profiledCallSiteId);
  2456. inlineeJitTimeData = jitTimeData;
  2457. doShareJitTimeData = true;
  2458. // If a recursive inliner has multiple recursive inlinees and if they hit the InlineCountMax
  2459. // threshold, then runtimeData for the inlinees may not be available (bug 2269097) for the inlinees
  2460. // as InlineCountMax threshold heuristics has higher priority than recursive inline heuristics. Since
  2461. // we share runtime data between recursive inliner and recursive inlinees, and all the call sites
  2462. // are recursive (we only do recursive inlining for functions where all the callsites are recursive),
  2463. // we can iterate over all the callsites of the inliner and setup the runtime data recursive inlinee chain
  2464. for (Js::ProfileId id = 0; id < profiledCallSiteCount; id++)
  2465. {
  2466. inlineeRuntimeData->SetupRecursiveInlineeChain(recycler, id, inlineeFunctionBody);
  2467. }
  2468. }
  2469. else
  2470. {
  2471. inlineeJitTimeData = jitTimeData->AddInlinee(recycler, profiledCallSiteId, inlinee);
  2472. }
  2473. GatherCodeGenData<true>(
  2474. recycler,
  2475. topFunctionBody,
  2476. inlineeFunctionBody,
  2477. entryPoint,
  2478. inliningDecider,
  2479. objTypeSpecFldInfoList,
  2480. inlineeJitTimeData,
  2481. inlineeRuntimeData,
  2482. fixedFunctionObject,
  2483. doShareJitTimeData,
  2484. functionBody == inlineeFunctionBody ? recursiveInlineDepth + 1 : 0);
  2485. if (jitTimeData != inlineeJitTimeData)
  2486. {
  2487. AddInlineCacheStats(jitTimeData, inlineeJitTimeData);
  2488. }
  2489. }
  2490. }
  2491. else
  2492. {
  2493. Js::FunctionCodeGenJitTimeData *const inlineeJitTimeData = jitTimeData->AddInlinee(recycler, profiledCallSiteId, inlinee);
  2494. GatherCodeGenData<true>(
  2495. recycler,
  2496. topFunctionBody,
  2497. inlineeFunctionBody,
  2498. entryPoint,
  2499. inliningDecider,
  2500. objTypeSpecFldInfoList,
  2501. inlineeJitTimeData,
  2502. IsInlinee
  2503. ? runtimeData->EnsureInlinee(recycler, profiledCallSiteId, inlineeFunctionBody)
  2504. : functionBody->EnsureInlineeCodeGenRuntimeData(recycler, profiledCallSiteId, inlineeFunctionBody),
  2505. fixedFunctionObject);
  2506. AddInlineCacheStats(jitTimeData, inlineeJitTimeData);
  2507. }
  2508. }
  2509. }
  2510. // Iterate through inlineCache getter setter and apply call sites recursively and determine what should be inlined
  2511. if (inlineGetterSetter || inlineApplyTarget || inlineCallTarget)
  2512. {
  2513. for(uint inlineCacheIndex = 0; inlineCacheIndex < functionBody->GetInlineCacheCount(); ++inlineCacheIndex)
  2514. {
  2515. const auto cacheType = profileData->GetFldInfo(functionBody, inlineCacheIndex)->flags;
  2516. if(cacheType == Js::FldInfo_NoInfo)
  2517. {
  2518. continue;
  2519. }
  2520. bool getSetInlineCandidate = inlineGetterSetter && ((cacheType & Js::FldInfo_FromAccessor) != 0);
  2521. bool callApplyInlineCandidate = (inlineCallTarget || inlineApplyTarget) && ((cacheType & Js::FldInfo_FromAccessor) == 0);
  2522. // 1. Do not inline if the x in a.x is both a getter/setter and is followed by a .apply
  2523. // 2. If we were optimistic earlier in assuming that the inline caches on the function object would be monomorphic and asserted that we may possibly inline apply target,
  2524. // then even if the field info flags say that the field access may be polymorphic, carry that optimism forward and try to inline apply target.
  2525. if (getSetInlineCandidate ^ callApplyInlineCandidate)
  2526. {
  2527. Js::ObjTypeSpecFldInfo* info = jitTimeData->GetObjTypeSpecFldInfoArray()->GetInfo(functionBody, inlineCacheIndex);
  2528. if (info == nullptr)
  2529. {
  2530. continue;
  2531. }
  2532. if (!(getSetInlineCandidate && info->UsesAccessor()) && !(callApplyInlineCandidate && !info->IsPoly()))
  2533. {
  2534. continue;
  2535. }
  2536. Js::JavascriptFunction* inlineeFunction = info->GetFieldValueAsFunctionIfAvailable();
  2537. if (inlineeFunction == nullptr)
  2538. {
  2539. continue;
  2540. }
  2541. Js::FunctionInfo* inlineeFunctionInfo = inlineeFunction->GetFunctionInfo();
  2542. Js::FunctionProxy* inlineeFunctionProxy = inlineeFunctionInfo->GetFunctionProxy();
  2543. if (inlineeFunctionProxy != nullptr && !functionBody->CheckCalleeContextForInlining(inlineeFunctionProxy))
  2544. {
  2545. continue;
  2546. }
  2547. const auto inlinee = inliningDecider.Inline(functionBody, inlineeFunctionInfo, false /*isConstructorCall*/, false /*isPolymorphicCall*/, 0, (uint16)inlineCacheIndex, 0, false);
  2548. if(!inlinee)
  2549. {
  2550. continue;
  2551. }
  2552. const auto inlineeFunctionBody = inlinee->GetFunctionBody();
  2553. if(!inlineeFunctionBody)
  2554. {
  2555. if ((
  2556. #ifdef ENABLE_DOM_FAST_PATH
  2557. inlinee->GetLocalFunctionId() == Js::JavascriptBuiltInFunction::DOMFastPathGetter ||
  2558. inlinee->GetLocalFunctionId() == Js::JavascriptBuiltInFunction::DOMFastPathSetter ||
  2559. #endif
  2560. (inlineeFunctionInfo->GetAttributes() & Js::FunctionInfo::Attributes::BuiltInInlinableAsLdFldInlinee) != 0) &&
  2561. !isJitTimeDataComputed)
  2562. {
  2563. jitTimeData->AddLdFldInlinee(recycler, inlineCacheIndex, inlinee);
  2564. }
  2565. continue;
  2566. }
  2567. Js::FunctionCodeGenRuntimeData *const inlineeRuntimeData = IsInlinee ? runtimeData->EnsureLdFldInlinee(recycler, inlineCacheIndex, inlineeFunctionBody) :
  2568. functionBody->EnsureLdFldInlineeCodeGenRuntimeData(recycler, inlineCacheIndex, inlineeFunctionBody);
  2569. if (inlineeRuntimeData->GetFunctionBody() != inlineeFunctionBody)
  2570. {
  2571. //There are obscure cases where profileData has not yet seen the polymorphic LdFld but the inlineCache has the newer object from which getter is invoked.
  2572. //In this case we don't want to inline that getter. Polymorphic bit will be set later correctly.
  2573. //See WinBlue 54540
  2574. continue;
  2575. }
  2576. Js::FunctionCodeGenJitTimeData *inlineeJitTimeData = jitTimeData->AddLdFldInlinee(recycler, inlineCacheIndex, inlinee);
  2577. GatherCodeGenData<true>(
  2578. recycler,
  2579. topFunctionBody,
  2580. inlineeFunctionBody,
  2581. entryPoint,
  2582. inliningDecider,
  2583. objTypeSpecFldInfoList,
  2584. inlineeJitTimeData,
  2585. inlineeRuntimeData,
  2586. nullptr);
  2587. AddInlineCacheStats(jitTimeData, inlineeJitTimeData);
  2588. }
  2589. }
  2590. }
  2591. #ifdef FIELD_ACCESS_STATS
  2592. if (PHASE_VERBOSE_TRACE(Js::ObjTypeSpecPhase, topFunctionBody) || PHASE_VERBOSE_TRACE(Js::EquivObjTypeSpecPhase, topFunctionBody))
  2593. {
  2594. if (jitTimeData->inlineCacheStats)
  2595. {
  2596. Output::Print(_u("ObTypeSpec: gathered code gen data for function %s (#%u) inlined %s (#%u): inline cache stats:\n"),
  2597. topFunctionBody->GetDisplayName(), topFunctionBody->GetFunctionNumber(), functionBody->GetDisplayName(), functionBody->GetFunctionNumber());
  2598. Output::Print(_u(" overall: total %u, no profile info %u\n"),
  2599. jitTimeData->inlineCacheStats->totalInlineCacheCount, jitTimeData->inlineCacheStats->noInfoInlineCacheCount);
  2600. Output::Print(_u(" mono: total %u, empty %u, cloned %u\n"),
  2601. jitTimeData->inlineCacheStats->monoInlineCacheCount, jitTimeData->inlineCacheStats->emptyMonoInlineCacheCount,
  2602. jitTimeData->inlineCacheStats->clonedMonoInlineCacheCount);
  2603. Output::Print(_u(" poly: total %u (high %u, low %u), empty %u, equivalent %u, cloned %u\n"),
  2604. jitTimeData->inlineCacheStats->polyInlineCacheCount, jitTimeData->inlineCacheStats->highUtilPolyInlineCacheCount,
  2605. jitTimeData->inlineCacheStats->lowUtilPolyInlineCacheCount, jitTimeData->inlineCacheStats->emptyPolyInlineCacheCount,
  2606. jitTimeData->inlineCacheStats->equivPolyInlineCacheCount, jitTimeData->inlineCacheStats->clonedPolyInlineCacheCount);
  2607. }
  2608. else
  2609. {
  2610. Output::Print(_u("ObTypeSpec: function %s (%s): inline cache stats unavailable\n"), topFunctionBody->GetDisplayName(), topFunctionBody->GetDebugNumberSet(debugStringBuffer));
  2611. }
  2612. Output::Flush();
  2613. }
  2614. #endif
  2615. #undef SetInlineCacheCount
  2616. #undef IncInlineCacheCount
  2617. #undef AddInlineCacheStats
  2618. }
  2619. Js::CodeGenRecyclableData *
  2620. NativeCodeGenerator::GatherCodeGenData(Js::FunctionBody *const topFunctionBody, Js::FunctionBody *const functionBody, Js::EntryPointInfo *const entryPoint, CodeGenWorkItem* workItem, void* function)
  2621. {
  2622. ASSERT_THREAD();
  2623. Assert(functionBody);
  2624. #ifdef PROFILE_EXEC
  2625. class AutoProfile
  2626. {
  2627. private:
  2628. Js::ScriptContextProfiler *const codeGenProfiler;
  2629. public:
  2630. AutoProfile(Js::ScriptContextProfiler *const codeGenProfiler) : codeGenProfiler(codeGenProfiler)
  2631. {
  2632. ProfileBegin(codeGenProfiler, Js::DelayPhase);
  2633. ProfileBegin(codeGenProfiler, Js::GatherCodeGenDataPhase);
  2634. }
  2635. ~AutoProfile()
  2636. {
  2637. ProfileEnd(codeGenProfiler, Js::GatherCodeGenDataPhase);
  2638. ProfileEnd(codeGenProfiler, Js::DelayPhase);
  2639. }
  2640. } autoProfile(foregroundCodeGenProfiler);
  2641. #endif
  2642. UpdateJITState();
  2643. const auto recycler = scriptContext->GetRecycler();
  2644. {
  2645. const auto jitTimeData = RecyclerNew(recycler, Js::FunctionCodeGenJitTimeData, functionBody, entryPoint);
  2646. InliningDecider inliningDecider(functionBody, workItem->Type() == JsLoopBodyWorkItemType, functionBody->IsInDebugMode(), workItem->GetJitMode());
  2647. BEGIN_TEMP_ALLOCATOR(gatherCodeGenDataAllocator, scriptContext, _u("GatherCodeGenData"));
  2648. ObjTypeSpecFldInfoList* objTypeSpecFldInfoList = JitAnew(gatherCodeGenDataAllocator, ObjTypeSpecFldInfoList, gatherCodeGenDataAllocator);
  2649. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2650. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2651. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  2652. if (PHASE_TRACE(Js::ObjTypeSpecPhase, topFunctionBody) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, topFunctionBody))
  2653. {
  2654. Output::Print(_u("ObjTypeSpec: top function %s (%s), function %s (%s): GatherCodeGenData(): \n"),
  2655. topFunctionBody->GetDisplayName(), topFunctionBody->GetDebugNumberSet(debugStringBuffer), functionBody->GetDisplayName(), functionBody->GetDebugNumberSet(debugStringBuffer2));
  2656. }
  2657. #endif
  2658. GatherCodeGenData<false>(recycler, topFunctionBody, functionBody, entryPoint, inliningDecider, objTypeSpecFldInfoList, jitTimeData, nullptr, function ? Js::JavascriptFunction::FromVar(function) : nullptr, 0);
  2659. #ifdef FIELD_ACCESS_STATS
  2660. Js::FieldAccessStats* fieldAccessStats = entryPoint->EnsureFieldAccessStats(recycler);
  2661. fieldAccessStats->Add(jitTimeData->inlineCacheStats);
  2662. entryPoint->GetScriptContext()->RecordFieldAccessStats(topFunctionBody, fieldAccessStats);
  2663. #endif
  2664. #ifdef FIELD_ACCESS_STATS
  2665. if (PHASE_TRACE(Js::ObjTypeSpecPhase, topFunctionBody) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, topFunctionBody))
  2666. {
  2667. auto stats = jitTimeData->inlineCacheStats;
  2668. Output::Print(_u("ObjTypeSpec: gathered code gen data for function %s (%s): inline cache stats:\n"), topFunctionBody->GetDisplayName(), topFunctionBody->GetDebugNumberSet(debugStringBuffer));
  2669. Output::Print(_u(" overall: total %u, no profile info %u\n"), stats->totalInlineCacheCount, stats->noInfoInlineCacheCount);
  2670. Output::Print(_u(" mono: total %u, empty %u, cloned %u\n"),
  2671. stats->monoInlineCacheCount, stats->emptyMonoInlineCacheCount, stats->clonedMonoInlineCacheCount);
  2672. Output::Print(_u(" poly: total %u (high %u, low %u), null %u, empty %u, ignored %u, disabled %u, equivalent %u, non-equivalent %u, cloned %u\n"),
  2673. stats->polyInlineCacheCount, stats->highUtilPolyInlineCacheCount, stats->lowUtilPolyInlineCacheCount,
  2674. stats->nullPolyInlineCacheCount, stats->emptyPolyInlineCacheCount, stats->ignoredPolyInlineCacheCount, stats->disabledPolyInlineCacheCount,
  2675. stats->equivPolyInlineCacheCount, stats->nonEquivPolyInlineCacheCount, stats->clonedPolyInlineCacheCount);
  2676. }
  2677. #endif
  2678. uint objTypeSpecFldInfoCount = objTypeSpecFldInfoList->Count();
  2679. jitTimeData->SetGlobalObjTypeSpecFldInfoArray(RecyclerNewArray(recycler, Js::ObjTypeSpecFldInfo*, objTypeSpecFldInfoCount), objTypeSpecFldInfoCount);
  2680. uint propertyInfoId = objTypeSpecFldInfoCount - 1;
  2681. FOREACH_SLISTCOUNTED_ENTRY(Js::ObjTypeSpecFldInfo*, info, objTypeSpecFldInfoList)
  2682. {
  2683. // Clear field values we don't need so we don't unnecessarily pin them while JIT-ing.
  2684. if (!info->GetKeepFieldValue() && !(info->IsPoly() && info->DoesntHaveEquivalence()))
  2685. {
  2686. info->SetFieldValue(nullptr);
  2687. }
  2688. jitTimeData->SetGlobalObjTypeSpecFldInfo(propertyInfoId--, info);
  2689. }
  2690. NEXT_SLISTCOUNTED_ENTRY;
  2691. END_TEMP_ALLOCATOR(gatherCodeGenDataAllocator, scriptContext);
  2692. auto jitData = workItem->GetJITData();
  2693. JITTimePolymorphicInlineCacheInfo::InitializeEntryPointPolymorphicInlineCacheInfo(
  2694. recycler,
  2695. entryPoint->EnsurePolymorphicInlineCacheInfo(recycler, workItem->GetFunctionBody()),
  2696. jitData);
  2697. jitTimeData->SetPolymorphicInlineInfo(jitData->inlineeInfo, jitData->selfInfo, jitData->selfInfo->polymorphicInlineCaches);
  2698. return RecyclerNew(recycler, Js::CodeGenRecyclableData, jitTimeData);
  2699. }
  2700. }
  2701. bool
  2702. NativeCodeGenerator::IsBackgroundJIT() const
  2703. {
  2704. return Processor()->ProcessesInBackground();
  2705. }
  2706. void
  2707. NativeCodeGenerator::EnterScriptStart()
  2708. {
  2709. // We should be in execution
  2710. Assert(scriptContext->GetThreadContext()->IsScriptActive());
  2711. Assert(scriptContext->GetThreadContext()->IsInScript());
  2712. if(CONFIG_FLAG(BgJitDelay) == 0 ||
  2713. Js::Configuration::Global.flags.EnforceExecutionModeLimits ||
  2714. scriptContext->GetThreadContext()->GetCallRootLevel() > 2)
  2715. {
  2716. return;
  2717. }
  2718. if (pendingCodeGenWorkItems == 0 || pendingCodeGenWorkItems > (uint)CONFIG_FLAG(BgJitPendingFuncCap))
  2719. {
  2720. // We have already finish code gen for this script context
  2721. // Only wait if the script is small and we can easily pre-JIT all of it.
  2722. return;
  2723. }
  2724. if (this->IsClosed())
  2725. {
  2726. return;
  2727. }
  2728. // Don't need to do anything if we're in debug mode
  2729. if (this->scriptContext->IsScriptContextInDebugMode() && !Js::Configuration::Global.EnableJitInDebugMode())
  2730. {
  2731. return;
  2732. }
  2733. // We've already done a few calls to this scriptContext, don't bother waiting.
  2734. if (scriptContext->callCount >= 3)
  2735. {
  2736. return;
  2737. }
  2738. scriptContext->callCount++;
  2739. if (scriptContext->GetDeferredBody())
  2740. {
  2741. OUTPUT_TRACE(Js::DelayPhase, _u("No delay because the script has a deferred body\n"));
  2742. return;
  2743. }
  2744. if(CONFIG_FLAG(BgJitDelayFgBuffer) >= CONFIG_FLAG(BgJitDelay))
  2745. {
  2746. return;
  2747. }
  2748. class AutoCleanup
  2749. {
  2750. private:
  2751. Js::ScriptContextProfiler *const codeGenProfiler;
  2752. public:
  2753. AutoCleanup(Js::ScriptContextProfiler *const codeGenProfiler) : codeGenProfiler(codeGenProfiler)
  2754. {
  2755. JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_DELAY_START(this, 0));
  2756. #ifdef PROFILE_EXEC
  2757. ProfileBegin(codeGenProfiler, Js::DelayPhase);
  2758. ProfileBegin(codeGenProfiler, Js::SpeculationPhase);
  2759. #endif
  2760. }
  2761. ~AutoCleanup()
  2762. {
  2763. #ifdef PROFILE_EXEC
  2764. ProfileEnd(codeGenProfiler, Js::SpeculationPhase);
  2765. ProfileEnd(codeGenProfiler, Js::DelayPhase);
  2766. #endif
  2767. JS_ETW(EventWriteJSCRIPT_NATIVECODEGEN_DELAY_STOP(this, 0));
  2768. }
  2769. } autoCleanup(
  2770. #ifdef PROFILE_EXEC
  2771. this->foregroundCodeGenProfiler
  2772. #else
  2773. nullptr
  2774. #endif
  2775. );
  2776. Processor()->PrioritizeManagerAndWait(this, CONFIG_FLAG(BgJitDelay) - CONFIG_FLAG(BgJitDelayFgBuffer));
  2777. }
  2778. void
  2779. FreeNativeCodeGenAllocation(Js::ScriptContext *scriptContext, Js::JavascriptMethod address)
  2780. {
  2781. if (!scriptContext->GetNativeCodeGenerator())
  2782. {
  2783. return;
  2784. }
  2785. scriptContext->GetNativeCodeGenerator()->QueueFreeNativeCodeGenAllocation((void*)address);
  2786. }
  2787. bool TryReleaseNonHiPriWorkItem(Js::ScriptContext* scriptContext, CodeGenWorkItem* workItem)
  2788. {
  2789. if (!scriptContext->GetNativeCodeGenerator())
  2790. {
  2791. return false;
  2792. }
  2793. return scriptContext->GetNativeCodeGenerator()->TryReleaseNonHiPriWorkItem(workItem);
  2794. }
  2795. // Called from within the lock
  2796. // The work item cannot be used after this point if it returns true
  2797. bool NativeCodeGenerator::TryReleaseNonHiPriWorkItem(CodeGenWorkItem* workItem)
  2798. {
  2799. // If its the highest priority, don't release it, let the job continue
  2800. if (workItem->IsInJitQueue())
  2801. {
  2802. return false;
  2803. }
  2804. workItems.Unlink(workItem);
  2805. Assert(!workItem->RecyclableData());
  2806. workItem->Delete();
  2807. return true;
  2808. }
  2809. void
  2810. NativeCodeGenerator::FreeNativeCodeGenAllocation(void* address)
  2811. {
  2812. if(this->backgroundAllocators)
  2813. {
  2814. ThreadContext * context = this->scriptContext->GetThreadContext();
  2815. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  2816. {
  2817. JITManager::GetJITManager()->FreeAllocation(context->GetRemoteThreadContextAddr(), (intptr_t)address);
  2818. }
  2819. else
  2820. {
  2821. this->backgroundAllocators->emitBufferManager.FreeAllocation(address);
  2822. }
  2823. }
  2824. }
  2825. void
  2826. NativeCodeGenerator::QueueFreeNativeCodeGenAllocation(void* address)
  2827. {
  2828. ASSERT_THREAD();
  2829. if(IsClosed())
  2830. {
  2831. return;
  2832. }
  2833. if (!JITManager::GetJITManager()->IsOOPJITEnabled())
  2834. {
  2835. //DeRegister Entry Point for CFG
  2836. ThreadContext::GetContextForCurrentThread()->SetValidCallTargetForCFG(address, false);
  2837. }
  2838. // The foreground allocators may have been used
  2839. ThreadContext * context = this->scriptContext->GetThreadContext();
  2840. if(this->foregroundAllocators)
  2841. {
  2842. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  2843. {
  2844. // TODO: OOP JIT, should we always just queue this in background?
  2845. JITManager::GetJITManager()->FreeAllocation(context->GetRemoteThreadContextAddr(), (intptr_t)address);
  2846. return;
  2847. }
  2848. else
  2849. {
  2850. if (this->foregroundAllocators->emitBufferManager.FreeAllocation(address))
  2851. {
  2852. return;
  2853. }
  2854. }
  2855. }
  2856. // The background allocators were used. Queue a job to free the allocation from the background thread.
  2857. this->freeLoopBodyManager.QueueFreeLoopBodyJob(address);
  2858. }
  2859. void NativeCodeGenerator::FreeLoopBodyJobManager::QueueFreeLoopBodyJob(void* codeAddress)
  2860. {
  2861. Assert(!this->isClosed);
  2862. FreeLoopBodyJob* job = HeapNewNoThrow(FreeLoopBodyJob, this, codeAddress);
  2863. if (job == nullptr)
  2864. {
  2865. FreeLoopBodyJob stackJob(this, codeAddress, false /* heapAllocated */);
  2866. {
  2867. AutoOptionalCriticalSection lock(Processor()->GetCriticalSection());
  2868. #if DBG
  2869. this->waitingForStackJob = true;
  2870. #endif
  2871. this->stackJobProcessed = false;
  2872. Processor()->AddJob(&stackJob);
  2873. }
  2874. Processor()->PrioritizeJobAndWait(this, &stackJob);
  2875. }
  2876. else
  2877. {
  2878. AutoOptionalCriticalSection lock(Processor()->GetCriticalSection());
  2879. if (Processor()->HasManager(this))
  2880. {
  2881. Processor()->AddJobAndProcessProactively<FreeLoopBodyJobManager, FreeLoopBodyJob*>(this, job);
  2882. }
  2883. else
  2884. {
  2885. HeapDelete(job);
  2886. }
  2887. }
  2888. }
  2889. #ifdef PROFILE_EXEC
  2890. void
  2891. NativeCodeGenerator::CreateProfiler(Js::ScriptContextProfiler * profiler)
  2892. {
  2893. Assert(this->foregroundCodeGenProfiler == nullptr);
  2894. this->foregroundCodeGenProfiler = profiler;
  2895. profiler->AddRef();
  2896. }
  2897. Js::ScriptContextProfiler *
  2898. NativeCodeGenerator::EnsureForegroundCodeGenProfiler()
  2899. {
  2900. if (Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag))
  2901. {
  2902. Assert(this->foregroundCodeGenProfiler != nullptr);
  2903. Assert(this->foregroundCodeGenProfiler->IsInitialized());
  2904. }
  2905. return this->foregroundCodeGenProfiler;
  2906. }
  2907. void
  2908. NativeCodeGenerator::SetProfilerFromNativeCodeGen(NativeCodeGenerator * nativeCodeGen)
  2909. {
  2910. Assert(Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag));
  2911. Assert(this->foregroundCodeGenProfiler != nullptr);
  2912. Assert(this->foregroundCodeGenProfiler->IsInitialized());
  2913. Assert(nativeCodeGen->foregroundCodeGenProfiler != nullptr);
  2914. Assert(nativeCodeGen->foregroundCodeGenProfiler->IsInitialized());
  2915. this->foregroundCodeGenProfiler->Release();
  2916. this->foregroundCodeGenProfiler = nativeCodeGen->foregroundCodeGenProfiler;
  2917. this->foregroundCodeGenProfiler->AddRef();
  2918. }
  2919. void
  2920. NativeCodeGenerator::ProfilePrint()
  2921. {
  2922. Js::ScriptContextProfiler *codegenProfiler = this->backgroundCodeGenProfiler;
  2923. if (Js::Configuration::Global.flags.Verbose)
  2924. {
  2925. //Print individual CodegenProfiler information in verbose mode
  2926. while (codegenProfiler)
  2927. {
  2928. codegenProfiler->ProfilePrint(Js::Configuration::Global.flags.Profile.GetFirstPhase());
  2929. codegenProfiler = codegenProfiler->next;
  2930. }
  2931. }
  2932. else
  2933. {
  2934. //Merge all the codegenProfiler for single snapshot.
  2935. Js::ScriptContextProfiler* mergeToProfiler = codegenProfiler;
  2936. // find the first initialized profiler
  2937. while (mergeToProfiler != nullptr && !mergeToProfiler->IsInitialized())
  2938. {
  2939. mergeToProfiler = mergeToProfiler->next;
  2940. }
  2941. if (mergeToProfiler != nullptr)
  2942. {
  2943. // merge the rest profiler to the above initialized profiler
  2944. codegenProfiler = mergeToProfiler->next;
  2945. while (codegenProfiler)
  2946. {
  2947. if (codegenProfiler->IsInitialized())
  2948. {
  2949. mergeToProfiler->ProfileMerge(codegenProfiler);
  2950. }
  2951. codegenProfiler = codegenProfiler->next;
  2952. }
  2953. mergeToProfiler->ProfilePrint(Js::Configuration::Global.flags.Profile.GetFirstPhase());
  2954. }
  2955. }
  2956. }
  2957. void
  2958. NativeCodeGenerator::ProfileBegin(Js::ScriptContextProfiler *const profiler, Js::Phase phase)
  2959. {
  2960. AssertMsg((profiler != nullptr) == Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag),
  2961. "Profiler tag is supplied but the profiler pointer is NULL");
  2962. if (profiler)
  2963. {
  2964. profiler->ProfileBegin(phase);
  2965. }
  2966. }
  2967. void
  2968. NativeCodeGenerator::ProfileEnd(Js::ScriptContextProfiler *const profiler, Js::Phase phase)
  2969. {
  2970. AssertMsg((profiler != nullptr) == Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag),
  2971. "Profiler tag is supplied but the profiler pointer is NULL");
  2972. if (profiler)
  2973. {
  2974. profiler->ProfileEnd(phase);
  2975. }
  2976. }
  2977. #endif
  2978. void NativeCodeGenerator::AddToJitQueue(CodeGenWorkItem *const codeGenWorkItem, bool prioritize, bool lock, void* function)
  2979. {
  2980. codeGenWorkItem->VerifyJitMode();
  2981. Js::CodeGenRecyclableData* recyclableData = GatherCodeGenData(codeGenWorkItem->GetFunctionBody(), codeGenWorkItem->GetFunctionBody(), codeGenWorkItem->GetEntryPoint(), codeGenWorkItem, function);
  2982. codeGenWorkItem->SetRecyclableData(recyclableData);
  2983. AutoOptionalCriticalSection autoLock(lock ? Processor()->GetCriticalSection() : nullptr);
  2984. scriptContext->GetThreadContext()->RegisterCodeGenRecyclableData(recyclableData);
  2985. // If we have added a lot of jobs that are still waiting to be jitted, remove the oldest job
  2986. // to ensure we do not spend time jitting stale work items.
  2987. const ExecutionMode jitMode = codeGenWorkItem->GetJitMode();
  2988. if(jitMode == ExecutionMode::FullJit &&
  2989. queuedFullJitWorkItemCount >= (unsigned int)CONFIG_FLAG(JitQueueThreshold))
  2990. {
  2991. CodeGenWorkItem *const workItemRemoved = queuedFullJitWorkItems.Tail()->WorkItem();
  2992. Assert(workItemRemoved->GetJitMode() == ExecutionMode::FullJit);
  2993. if(Processor()->RemoveJob(workItemRemoved))
  2994. {
  2995. queuedFullJitWorkItems.UnlinkFromEnd();
  2996. --queuedFullJitWorkItemCount;
  2997. workItemRemoved->OnRemoveFromJitQueue(this);
  2998. }
  2999. }
  3000. Processor()->AddJob(codeGenWorkItem, prioritize); // This one can throw (really unlikely though), OOM specifically.
  3001. if(jitMode == ExecutionMode::FullJit)
  3002. {
  3003. QueuedFullJitWorkItem *const queuedFullJitWorkItem = codeGenWorkItem->EnsureQueuedFullJitWorkItem();
  3004. if(queuedFullJitWorkItem) // ignore OOM, this work item just won't be removed from the job processor's queue
  3005. {
  3006. if(prioritize)
  3007. {
  3008. queuedFullJitWorkItems.LinkToBeginning(queuedFullJitWorkItem);
  3009. }
  3010. else
  3011. {
  3012. queuedFullJitWorkItems.LinkToEnd(queuedFullJitWorkItem);
  3013. }
  3014. ++queuedFullJitWorkItemCount;
  3015. }
  3016. }
  3017. codeGenWorkItem->OnAddToJitQueue();
  3018. }
  3019. void NativeCodeGenerator::AddWorkItem(CodeGenWorkItem* workitem)
  3020. {
  3021. workitem->ResetJitMode();
  3022. workItems.LinkToEnd(workitem);
  3023. }
  3024. Js::ScriptContextProfiler * NativeCodeGenerator::GetBackgroundCodeGenProfiler(PageAllocator *allocator)
  3025. {
  3026. #ifdef PROFILE_EXEC
  3027. if (Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag))
  3028. {
  3029. Js::ScriptContextProfiler *codegenProfiler = this->backgroundCodeGenProfiler;
  3030. while (codegenProfiler)
  3031. {
  3032. if (codegenProfiler->pageAllocator == allocator)
  3033. {
  3034. if (!codegenProfiler->IsInitialized())
  3035. {
  3036. codegenProfiler->Initialize(allocator, nullptr);
  3037. }
  3038. return codegenProfiler;
  3039. }
  3040. codegenProfiler = codegenProfiler->next;
  3041. }
  3042. Assert(false);
  3043. }
  3044. return nullptr;
  3045. #else
  3046. return nullptr;
  3047. #endif
  3048. }
  3049. void NativeCodeGenerator::AllocateBackgroundCodeGenProfiler(PageAllocator *pageAllocator)
  3050. {
  3051. #ifdef PROFILE_EXEC
  3052. if (Js::Configuration::Global.flags.IsEnabled(Js::ProfileFlag))
  3053. {
  3054. Js::ScriptContextProfiler *codegenProfiler = NoCheckHeapNew(Js::ScriptContextProfiler);
  3055. codegenProfiler->pageAllocator = pageAllocator;
  3056. codegenProfiler->next = this->backgroundCodeGenProfiler;
  3057. this->backgroundCodeGenProfiler = codegenProfiler;
  3058. }
  3059. #endif
  3060. }
  3061. bool NativeCodeGenerator::TryAggressiveInlining(Js::FunctionBody *const topFunctionBody, Js::FunctionBody *const inlineeFunctionBody, InliningDecider &inliningDecider, uint& inlineeCount, uint recursiveInlineDepth)
  3062. {
  3063. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  3064. if (!inlineeFunctionBody->GetProfiledCallSiteCount())
  3065. {
  3066. // Nothing to inline. See this as fully inlinable function.
  3067. return true;
  3068. }
  3069. class AutoCleanup
  3070. {
  3071. private:
  3072. Js::FunctionBody *const functionBody;
  3073. public:
  3074. AutoCleanup(Js::FunctionBody *const functionBody) : functionBody(functionBody)
  3075. {
  3076. functionBody->OnBeginInlineInto();
  3077. }
  3078. ~AutoCleanup()
  3079. {
  3080. functionBody->OnEndInlineInto();
  3081. }
  3082. } autoCleanup(inlineeFunctionBody);
  3083. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  3084. class AutoTrace
  3085. {
  3086. Js::FunctionBody *const topFunc;
  3087. Js::FunctionBody *const inlineeFunc;
  3088. uint32& inlineeCount;
  3089. bool done;
  3090. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  3091. char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  3092. public:
  3093. AutoTrace(Js::FunctionBody *const topFunctionBody, Js::FunctionBody *const inlineeFunctionBody, uint32& inlineeCount) : topFunc(topFunctionBody),
  3094. inlineeFunc(inlineeFunctionBody), done(false), inlineeCount(inlineeCount)
  3095. {
  3096. if (topFunc == inlineeFunc)
  3097. {
  3098. INLINE_TESTTRACE(_u("INLINING: Recursive tryAggressiveInlining started topFunc: %s (%s)\n"), topFunc->GetDisplayName(),
  3099. topFunc->GetDebugNumberSet(debugStringBuffer))
  3100. }
  3101. }
  3102. void Done(bool success)
  3103. {
  3104. if (success)
  3105. {
  3106. done = true;
  3107. if (topFunc == inlineeFunc)
  3108. {
  3109. INLINE_TESTTRACE(_u("INLINING: Recursive tryAggressiveInlining succeeded topFunc: %s (%s), inlinee count: %d\n"), topFunc->GetDisplayName(),
  3110. topFunc->GetDebugNumberSet(debugStringBuffer), inlineeCount);
  3111. }
  3112. else
  3113. {
  3114. INLINE_TESTTRACE(_u("INLINING: TryAggressiveInlining succeeded topFunc: %s (%s), inlinee: %s (%s) \n"), topFunc->GetDisplayName(),
  3115. topFunc->GetDebugNumberSet(debugStringBuffer),
  3116. inlineeFunc->GetDisplayName(),
  3117. inlineeFunc->GetDebugNumberSet(debugStringBuffer2));
  3118. }
  3119. }
  3120. else
  3121. {
  3122. Assert(done == false);
  3123. }
  3124. }
  3125. void TraceFailure(const char16 *message)
  3126. {
  3127. INLINE_TESTTRACE(_u("INLINING: TryAggressiveInlining failed topFunc (%s): %s (%s), inlinee: %s (%s) \n"), message, topFunc->GetDisplayName(),
  3128. topFunc->GetDebugNumberSet(debugStringBuffer),
  3129. inlineeFunc->GetDisplayName(),
  3130. inlineeFunc->GetDebugNumberSet(debugStringBuffer2));
  3131. }
  3132. ~AutoTrace()
  3133. {
  3134. if (!done)
  3135. {
  3136. if (topFunc == inlineeFunc)
  3137. {
  3138. INLINE_TESTTRACE(_u("INLINING: Recursive tryAggressiveInlining failed topFunc: %s (%s)\n"), topFunc->GetDisplayName(),
  3139. topFunc->GetDebugNumberSet(debugStringBuffer));
  3140. }
  3141. else
  3142. {
  3143. INLINE_TESTTRACE(_u("INLINING: TryAggressiveInlining failed topFunc: %s (%s), inlinee: %s (%s) \n"), topFunc->GetDisplayName(),
  3144. topFunc->GetDebugNumberSet(debugStringBuffer),
  3145. inlineeFunc->GetDisplayName(),
  3146. inlineeFunc->GetDebugNumberSet(debugStringBuffer2));
  3147. }
  3148. }
  3149. }
  3150. };
  3151. AutoTrace trace(topFunctionBody, inlineeFunctionBody, inlineeCount);
  3152. #endif
  3153. if (inlineeFunctionBody->GetProfiledSwitchCount())
  3154. {
  3155. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  3156. trace.TraceFailure(_u("Switch statement in inlinee"));
  3157. #endif
  3158. return false;
  3159. }
  3160. bool isInlinee = topFunctionBody != inlineeFunctionBody;
  3161. if (isInlinee ? !inliningDecider.InlineIntoInliner(inlineeFunctionBody) : !inliningDecider.InlineIntoTopFunc())
  3162. {
  3163. return false;
  3164. }
  3165. const auto profiledCallSiteCount = inlineeFunctionBody->GetProfiledCallSiteCount();
  3166. for (Js::ProfileId profiledCallSiteId = 0; profiledCallSiteId < profiledCallSiteCount; ++profiledCallSiteId)
  3167. {
  3168. bool isConstructorCall = false;
  3169. bool isPolymorphicCall = false;
  3170. if (!inliningDecider.HasCallSiteInfo(inlineeFunctionBody, profiledCallSiteId))
  3171. {
  3172. //There is no callsite information. We should hit bailonnoprofile for these callsites. Ignore.
  3173. continue;
  3174. }
  3175. Js::FunctionInfo *inlinee = inliningDecider.GetCallSiteFuncInfo(inlineeFunctionBody, profiledCallSiteId, &isConstructorCall, &isPolymorphicCall);
  3176. if (!inlinee)
  3177. {
  3178. if (isPolymorphicCall)
  3179. {
  3180. //Try and see if this polymorphic call
  3181. Js::FunctionBody* inlineeFunctionBodyArray[Js::DynamicProfileInfo::maxPolymorphicInliningSize] = { 0 };
  3182. bool canInlineArray[Js::DynamicProfileInfo::maxPolymorphicInliningSize] = { 0 };
  3183. uint polyInlineeCount = inliningDecider.InlinePolymorphicCallSite(inlineeFunctionBody, profiledCallSiteId, inlineeFunctionBodyArray,
  3184. Js::DynamicProfileInfo::maxPolymorphicInliningSize, canInlineArray);
  3185. //We should be able to inline everything here.
  3186. if (polyInlineeCount >= 2)
  3187. {
  3188. for (uint i = 0; i < polyInlineeCount; i++)
  3189. {
  3190. bool isInlined = canInlineArray[i];
  3191. if (isInlined)
  3192. {
  3193. ++inlineeCount;
  3194. if (!TryAggressiveInlining(topFunctionBody, inlineeFunctionBodyArray[i], inliningDecider, inlineeCount, inlineeFunctionBody == inlineeFunctionBodyArray[i] ? recursiveInlineDepth + 1 : 0))
  3195. {
  3196. return false;
  3197. }
  3198. }
  3199. else
  3200. {
  3201. return false;
  3202. }
  3203. }
  3204. }
  3205. else
  3206. {
  3207. return false;
  3208. }
  3209. }
  3210. else
  3211. {
  3212. return false;
  3213. }
  3214. }
  3215. else
  3216. {
  3217. inlinee = inliningDecider.Inline(inlineeFunctionBody, inlinee, isConstructorCall, false, inliningDecider.GetConstantArgInfo(inlineeFunctionBody, profiledCallSiteId), profiledCallSiteId, inlineeFunctionBody == inlinee ? recursiveInlineDepth + 1 : 0, true);
  3218. if (!inlinee)
  3219. {
  3220. return false;
  3221. }
  3222. Js::FunctionBody *const functionBody = inlinee->GetFunctionBody();
  3223. if (!functionBody)
  3224. {
  3225. //Built-in
  3226. continue;
  3227. }
  3228. //Recursive call
  3229. ++inlineeCount;
  3230. if (!TryAggressiveInlining(topFunctionBody, functionBody, inliningDecider, inlineeCount, inlineeFunctionBody == functionBody ? recursiveInlineDepth + 1 : 0 ))
  3231. {
  3232. return false;
  3233. }
  3234. }
  3235. }
  3236. #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
  3237. trace.Done(true);
  3238. #endif
  3239. return true;
  3240. }