GlobOptFields.cpp 128 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315
  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. /*
  7. Field Hoisting
  8. --------------
  9. The backward pass calculates field load values that are reachable from the loop top.
  10. It optimistically assumes that a[] doesn't kill any fields in the hopes that glob opt
  11. will have more information to not kill the field.
  12. During the forward pass the root prepass will assume that the field hoist candidate
  13. is live (set the livefields bitvector). (GlobOpt::PreparePrepassFieldHoisting)
  14. The "hoistable field" bitvector is used to keep track of whether the current instruction
  15. may have the initial field value at the loop top. If so, they are hoistable.
  16. Even when the value is only visible at the loop top from one path, there is benefit in
  17. hoisting the field.
  18. e.g 1. We can hoist the field in this case and get benefit:
  19. while {
  20. if {
  21. o.x = <== kills field value
  22. }
  23. = o.x <== only has the loop top field value on the "!if" path
  24. }
  25. After hoisting the field:
  26. s1 = o.x <== hoisted field load
  27. while {
  28. if {
  29. o.x =
  30. s1 = <== maintain the hoisted field value
  31. }
  32. = s1 <== avoided a field load
  33. }
  34. When we identify a field load as hoistable, we will add the instruction to a list on the loop.
  35. After the prepass, we will determine the fields that we are going to hoist from the
  36. field candidates. (GlobOpt::PrepareFieldHoisting)
  37. If it is not live - even if we detect a hoistable load - it is not beneficial to hoist
  38. the load as we will have to insert a field load to compensate for the loop back edges.
  39. We will just rely on field copy prop to optimize in that case.
  40. e.g 2. Hoisting this require us to add a field load back at the end of the loop with no benefit.
  41. while {
  42. = o.x <== hoistable field but isn't live on back edge
  43. b.x = <== kills o.x as o and b may be aliased
  44. }
  45. If it is live on back edge then it is possible to hoist the field load.
  46. e.g 3. Although the field is killed, if the value is live on back edge we can still hoist it.
  47. while {
  48. = o.x
  49. = o.x
  50. b.x =
  51. = o.x
  52. }
  53. After hoisting the field, s1 is live for the whole loop
  54. s1 = o.x
  55. while {
  56. = s1 <== eliminated one field load
  57. = s1 <== copy prop
  58. b.x =
  59. s1 = o.x
  60. }
  61. However, since our register allocator doesn't handle long lifetimes, copy prop may do a better job.
  62. We would only replace one field load - instead of two.
  63. (Currently we hoist in this case)
  64. e.g. 4. Live time of s1 is much shorter, which works better with our current register allocator.
  65. while {
  66. s1 = o.x
  67. = s1 <== copy prop
  68. b.x =
  69. s1 = o.x
  70. }
  71. May want to add heuristics to determine whether to hoist by looking at the number of field
  72. loads we can replace. See unittest\fieldopts\fieldhoist5.js for timing with various -off/-force
  73. of fieldhoist/fieldcopyprop. Currently, field hoist is better or the same as field copy prop
  74. except for kill_singleuse in the test where we can only eliminate one field load compared to copy prop
  75. If we ever improve the register allocator to do better, we might lift this restriction.
  76. In GlobOpt::PrepareFieldHoisting, we go through all the hoistable field loads that are live on the back edge.
  77. We create a preassigned symbol for the hoisted field and add it to the fieldHoistSymMap of the loop.
  78. If the value is live coming into the loop (via field copy prop, we will create the instruction to
  79. assign the value to the preassigned sym. (GlobOpt::HoistFieldLoadValue)
  80. If we don't know the value yet, we will create the load field instead. (GlobOpt::HoistFieldLoad)
  81. As we are processing instructions in the non-prepass, when we see a field load, if it is live already then we have
  82. a value in the preassigned sym and we can just replace the load. (GlobOpt::CopyPropHoistedFields)
  83. If it is not live, then we don't have the value of the field, so keep the field load, but also
  84. assign the loaded value to the preassigned sym. (GlobOpt::ReloadFieldHoistStackSym)
  85. If the instruction is a store of a hoisted field, then create an assignment of the value to the preassigned
  86. symbol to maintain a live field value. (GlobOpt::CopyStoreFieldHoistStackSym)
  87. */
  88. bool
  89. GlobOpt::DoFieldCopyProp() const
  90. {
  91. BasicBlock *block = this->currentBlock;
  92. Loop *loop = block->loop;
  93. if (this->isRecursiveCallOnLandingPad)
  94. {
  95. // The landing pad at this point only contains load hosted by PRE.
  96. // These need to be copy-prop'd into the loop.
  97. // We want to look at the implicit-call info of the loop, not it's parent.
  98. Assert(block->IsLandingPad());
  99. loop = block->next->loop;
  100. Assert(loop);
  101. }
  102. return DoFieldCopyProp(loop);
  103. }
  104. bool
  105. GlobOpt::DoFunctionFieldCopyProp() const
  106. {
  107. return DoFieldCopyProp(nullptr);
  108. }
  109. bool
  110. GlobOpt::DoFieldCopyProp(Loop * loop) const
  111. {
  112. if (PHASE_OFF(Js::CopyPropPhase, this->func))
  113. {
  114. // Can't do field copy prop without copy prop
  115. return false;
  116. }
  117. if (PHASE_FORCE(Js::FieldCopyPropPhase, this->func))
  118. {
  119. // Force always turns on field copy prop
  120. return true;
  121. }
  122. if (this->DoFieldHoisting(loop))
  123. {
  124. // Have to do field copy prop when we are doing field hoisting
  125. return true;
  126. }
  127. if (PHASE_OFF(Js::FieldCopyPropPhase, this->func))
  128. {
  129. return false;
  130. }
  131. return this->DoFieldOpts(loop);
  132. }
  133. bool
  134. GlobOpt::DoFieldHoisting(Loop *loop)
  135. {
  136. if (loop == nullptr)
  137. {
  138. return false;
  139. }
  140. Func * func = loop->GetHeadBlock()->GetFirstInstr()->m_func->GetTopFunc();
  141. if (PHASE_OFF(Js::CopyPropPhase, func))
  142. {
  143. // Can't do field hoisting without copy prop
  144. return false;
  145. }
  146. if (PHASE_OFF(Js::FieldHoistPhase, func))
  147. {
  148. return false;
  149. }
  150. if (!PHASE_OFF(Js::FieldPREPhase, func))
  151. {
  152. return false;
  153. }
  154. if (PHASE_FORCE(Js::FieldHoistPhase, func))
  155. {
  156. // Force always turns on field hoisting
  157. return true;
  158. }
  159. return loop->CanDoFieldHoist();
  160. }
  161. bool
  162. GlobOpt::DoFieldHoisting() const
  163. {
  164. return this->DoFieldHoisting(this->currentBlock->loop);
  165. }
  166. bool
  167. GlobOpt::DoObjTypeSpec() const
  168. {
  169. return this->DoObjTypeSpec(this->currentBlock->loop);
  170. }
  171. bool
  172. GlobOpt::DoObjTypeSpec(Loop *loop) const
  173. {
  174. if (!this->func->DoFastPaths())
  175. {
  176. return false;
  177. }
  178. if (PHASE_FORCE(Js::ObjTypeSpecPhase, this->func))
  179. {
  180. return true;
  181. }
  182. if (PHASE_OFF(Js::ObjTypeSpecPhase, this->func))
  183. {
  184. return false;
  185. }
  186. if (this->func->IsLoopBody() && this->func->HasProfileInfo() && this->func->GetReadOnlyProfileInfo()->IsObjTypeSpecDisabledInJitLoopBody())
  187. {
  188. return false;
  189. }
  190. if (this->ImplicitCallFlagsAllowOpts(this->func))
  191. {
  192. Assert(loop == nullptr || loop->CanDoFieldCopyProp());
  193. return true;
  194. }
  195. return loop != nullptr && loop->CanDoFieldCopyProp();
  196. }
  197. bool
  198. GlobOpt::DoFieldOpts(Loop * loop) const
  199. {
  200. if (this->ImplicitCallFlagsAllowOpts(this->func))
  201. {
  202. Assert(loop == nullptr || loop->CanDoFieldCopyProp());
  203. return true;
  204. }
  205. return loop != nullptr && loop->CanDoFieldCopyProp();
  206. }
  207. bool GlobOpt::DoFieldPRE() const
  208. {
  209. Loop *loop = this->currentBlock->loop;
  210. return DoFieldPRE(loop);
  211. }
  212. bool
  213. GlobOpt::DoFieldPRE(Loop *loop) const
  214. {
  215. if (PHASE_OFF(Js::FieldPREPhase, this->func))
  216. {
  217. return false;
  218. }
  219. if (PHASE_FORCE(Js::FieldPREPhase, func))
  220. {
  221. // Force always turns on field PRE
  222. return true;
  223. }
  224. return DoFieldOpts(loop);
  225. }
  226. bool GlobOpt::HasMemOp(Loop *loop)
  227. {
  228. #pragma prefast(suppress: 6285, "logical-or of constants is by design")
  229. return (
  230. loop &&
  231. loop->doMemOp &&
  232. (
  233. !PHASE_OFF(Js::MemSetPhase, this->func) ||
  234. !PHASE_OFF(Js::MemCopyPhase, this->func)
  235. ) &&
  236. loop->memOpInfo &&
  237. loop->memOpInfo->candidates &&
  238. !loop->memOpInfo->candidates->Empty()
  239. );
  240. }
  241. bool
  242. GlobOpt::TrackHoistableFields() const
  243. {
  244. return this->IsLoopPrePass() && this->currentBlock->loop == this->prePassLoop;
  245. }
  246. void
  247. GlobOpt::KillLiveFields(StackSym * stackSym, BVSparse<JitArenaAllocator> * bv)
  248. {
  249. if (stackSym->IsTypeSpec())
  250. {
  251. stackSym = stackSym->GetVarEquivSym(this->func);
  252. }
  253. Assert(stackSym);
  254. // If the sym has no objectSymInfo, it must not represent an object and, hence, has no type sym or
  255. // property syms to kill.
  256. if (!stackSym->HasObjectInfo())
  257. {
  258. return;
  259. }
  260. // Note that the m_writeGuardSym is killed here as well, because it is part of the
  261. // m_propertySymList of the object.
  262. ObjectSymInfo * objectSymInfo = stackSym->GetObjectInfo();
  263. PropertySym * propertySym = objectSymInfo->m_propertySymList;
  264. while (propertySym != nullptr)
  265. {
  266. Assert(propertySym->m_stackSym == stackSym);
  267. bv->Clear(propertySym->m_id);
  268. if (this->IsLoopPrePass())
  269. {
  270. for (Loop * loop = this->rootLoopPrePass; loop != nullptr; loop = loop->parent)
  271. {
  272. loop->fieldKilled->Set(propertySym->m_id);
  273. }
  274. }
  275. else if (bv->IsEmpty())
  276. {
  277. // shortcut
  278. break;
  279. }
  280. propertySym = propertySym->m_nextInStackSymList;
  281. }
  282. this->KillObjectType(stackSym, bv);
  283. }
  284. void
  285. GlobOpt::KillLiveFields(PropertySym * propertySym, BVSparse<JitArenaAllocator> * bv)
  286. {
  287. KillLiveFields(propertySym->m_propertyEquivSet, bv);
  288. }
  289. void GlobOpt::KillLiveFields(BVSparse<JitArenaAllocator> *const propertyEquivSet, BVSparse<JitArenaAllocator> *const bv) const
  290. {
  291. Assert(bv);
  292. if (propertyEquivSet)
  293. {
  294. bv->Minus(propertyEquivSet);
  295. if (this->IsLoopPrePass())
  296. {
  297. for (Loop * loop = this->rootLoopPrePass; loop != nullptr; loop = loop->parent)
  298. {
  299. loop->fieldKilled->Or(propertyEquivSet);
  300. }
  301. }
  302. }
  303. }
  304. void
  305. GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func)
  306. {
  307. IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
  308. // obj.x = 10;
  309. // obj["x"] = ...; // This needs to kill obj.x... We need to kill all fields...
  310. //
  311. // Also, 'arguments[i] =' needs to kill all slots even if 'i' is an int.
  312. //
  313. // NOTE: we only need to kill slots here, not all fields. It may be good to separate these one day.
  314. //
  315. // Regarding the check for type specialization:
  316. // - Type specialization does not always update the value to a definite type.
  317. // - The loop prepass is conservative on values even when type specialization occurs.
  318. // - We check the type specialization status for the sym as well. For the purpose of doing kills, we can assume that
  319. // if type specialization happened, that fields don't need to be killed. Note that they may be killed in the next
  320. // pass based on the value.
  321. if (func->GetThisOrParentInlinerHasArguments() ||
  322. (
  323. indexOpnd &&
  324. (
  325. indexOpnd->m_sym->m_isNotInt ||
  326. (inGlobOpt && !indexOpnd->GetValueType().IsNumber() && !IsTypeSpecialized(indexOpnd->m_sym, &blockData))
  327. )
  328. ))
  329. {
  330. this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms
  331. SetAnyPropertyMayBeWrittenTo();
  332. }
  333. }
  334. void
  335. GlobOpt::KillAllFields(BVSparse<JitArenaAllocator> * bv)
  336. {
  337. bv->ClearAll();
  338. if (this->IsLoopPrePass())
  339. {
  340. for (Loop * loop = this->rootLoopPrePass; loop != nullptr; loop = loop->parent)
  341. {
  342. loop->allFieldsKilled = true;
  343. }
  344. }
  345. }
  346. void
  347. GlobOpt::SetAnyPropertyMayBeWrittenTo()
  348. {
  349. this->func->anyPropertyMayBeWrittenTo = true;
  350. }
  351. void
  352. GlobOpt::AddToPropertiesWrittenTo(Js::PropertyId propertyId)
  353. {
  354. this->func->EnsurePropertiesWrittenTo();
  355. this->func->propertiesWrittenTo->Item(propertyId);
  356. }
  357. void
  358. GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bool inGlobOpt)
  359. {
  360. if (bv->IsEmpty() && (!this->IsLoopPrePass() || this->rootLoopPrePass->allFieldsKilled))
  361. {
  362. return;
  363. }
  364. if (instr->m_opcode == Js::OpCode::FromVar || instr->m_opcode == Js::OpCode::Conv_Prim)
  365. {
  366. return;
  367. }
  368. IR::Opnd * dstOpnd = instr->GetDst();
  369. if (dstOpnd)
  370. {
  371. if (dstOpnd->IsRegOpnd())
  372. {
  373. Sym * sym = dstOpnd->AsRegOpnd()->m_sym;
  374. if (sym->IsStackSym())
  375. {
  376. KillLiveFields(sym->AsStackSym(), bv);
  377. }
  378. }
  379. else if (dstOpnd->IsSymOpnd())
  380. {
  381. Sym * sym = dstOpnd->AsSymOpnd()->m_sym;
  382. if (sym->IsStackSym())
  383. {
  384. KillLiveFields(sym->AsStackSym(), bv);
  385. }
  386. else
  387. {
  388. Assert(sym->IsPropertySym());
  389. if (instr->m_opcode == Js::OpCode::InitLetFld || instr->m_opcode == Js::OpCode::InitConstFld || instr->m_opcode == Js::OpCode::InitFld)
  390. {
  391. // These can grow the aux slot of the activation object.
  392. // We need to kill the slot array sym as well.
  393. PropertySym * slotArraySym = PropertySym::Find(sym->AsPropertySym()->m_stackSym->m_id,
  394. (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var) /*, PropertyKindSlotArray */, instr->m_func);
  395. if (slotArraySym)
  396. {
  397. bv->Clear(slotArraySym->m_id);
  398. }
  399. }
  400. }
  401. }
  402. }
  403. if (bv->IsEmpty() && (!this->IsLoopPrePass() || this->rootLoopPrePass->allFieldsKilled))
  404. {
  405. return;
  406. }
  407. Sym *sym;
  408. IR::JnHelperMethod fnHelper;
  409. switch(instr->m_opcode)
  410. {
  411. case Js::OpCode::StElemI_A:
  412. case Js::OpCode::StElemI_A_Strict:
  413. Assert(dstOpnd != nullptr);
  414. KillLiveFields(this->lengthEquivBv, bv);
  415. KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
  416. break;
  417. case Js::OpCode::DeleteElemI_A:
  418. case Js::OpCode::DeleteElemIStrict_A:
  419. Assert(dstOpnd != nullptr);
  420. KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
  421. break;
  422. case Js::OpCode::DeleteFld:
  423. case Js::OpCode::DeleteRootFld:
  424. case Js::OpCode::DeleteFldStrict:
  425. case Js::OpCode::DeleteRootFldStrict:
  426. sym = instr->GetSrc1()->AsSymOpnd()->m_sym;
  427. KillLiveFields(sym->AsPropertySym(), bv);
  428. if (inGlobOpt)
  429. {
  430. AddToPropertiesWrittenTo(sym->AsPropertySym()->m_propertyId);
  431. this->KillAllObjectTypes(bv);
  432. }
  433. break;
  434. case Js::OpCode::InitSetFld:
  435. case Js::OpCode::InitGetFld:
  436. case Js::OpCode::InitClassMemberGet:
  437. case Js::OpCode::InitClassMemberSet:
  438. sym = instr->GetDst()->AsSymOpnd()->m_sym;
  439. KillLiveFields(sym->AsPropertySym(), bv);
  440. if (inGlobOpt)
  441. {
  442. AddToPropertiesWrittenTo(sym->AsPropertySym()->m_propertyId);
  443. this->KillAllObjectTypes(bv);
  444. }
  445. break;
  446. case Js::OpCode::StFld:
  447. case Js::OpCode::StRootFld:
  448. case Js::OpCode::StFldStrict:
  449. case Js::OpCode::StRootFldStrict:
  450. case Js::OpCode::StSlot:
  451. case Js::OpCode::StSlotChkUndecl:
  452. Assert(dstOpnd != nullptr);
  453. sym = dstOpnd->AsSymOpnd()->m_sym;
  454. if (inGlobOpt)
  455. {
  456. AddToPropertiesWrittenTo(sym->AsPropertySym()->m_propertyId);
  457. }
  458. if ((inGlobOpt && (sym->AsPropertySym()->m_propertyId == Js::PropertyIds::valueOf || sym->AsPropertySym()->m_propertyId == Js::PropertyIds::toString)) ||
  459. instr->CallsAccessor())
  460. {
  461. // If overriding valueof/tostring, we might have expected a previous LdFld to bailout on implicitCalls but didn't.
  462. // CSE's for example would have expected a bailout. Clear all fields to prevent optimizing across.
  463. this->KillAllFields(bv);
  464. }
  465. else
  466. {
  467. KillLiveFields(sym->AsPropertySym(), bv);
  468. }
  469. break;
  470. case Js::OpCode::InlineArrayPush:
  471. case Js::OpCode::InlineArrayPop:
  472. KillLiveFields(this->lengthEquivBv, bv);
  473. break;
  474. case Js::OpCode::InlineeStart:
  475. case Js::OpCode::InlineeEnd:
  476. Assert(!instr->UsesAllFields());
  477. // Kill all live 'arguments' and 'caller' fields, as 'inlineeFunction.arguments' and 'inlineeFunction.caller'
  478. // cannot be copy-propped across different instances of the same inlined function.
  479. KillLiveFields(argumentsEquivBv, bv);
  480. KillLiveFields(callerEquivBv, bv);
  481. break;
  482. case Js::OpCode::CallDirect:
  483. fnHelper = instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper;
  484. // Kill length field for built-ins that can update it.
  485. if(nullptr != this->lengthEquivBv && (fnHelper == IR::JnHelperMethod::HelperArray_Shift || fnHelper == IR::JnHelperMethod::HelperArray_Splice
  486. || fnHelper == IR::JnHelperMethod::HelperArray_Unshift))
  487. {
  488. KillLiveFields(this->lengthEquivBv, bv);
  489. }
  490. if ((fnHelper == IR::JnHelperMethod::HelperRegExp_Exec)
  491. || (fnHelper == IR::JnHelperMethod::HelperString_Match)
  492. || (fnHelper == IR::JnHelperMethod::HelperString_Replace))
  493. {
  494. // Consider: We may not need to kill all fields here.
  495. this->KillAllFields(bv);
  496. }
  497. break;
  498. default:
  499. if (instr->UsesAllFields())
  500. {
  501. // This also kills all property type values, as the same bit-vector tracks those stack syms.
  502. this->KillAllFields(bv);
  503. }
  504. break;
  505. }
  506. }
  507. void
  508. GlobOpt::ProcessFieldKills(IR::Instr * instr)
  509. {
  510. if (!this->DoFieldCopyProp() && !this->DoFieldRefOpts() && !DoCSE())
  511. {
  512. Assert(this->blockData.liveFields->IsEmpty());
  513. return;
  514. }
  515. ProcessFieldKills(instr, this->blockData.liveFields, true);
  516. if (this->blockData.hoistableFields)
  517. {
  518. Assert(this->TrackHoistableFields());
  519. // Fields that are killed are no longer hoistable.
  520. this->blockData.hoistableFields->And(this->blockData.liveFields);
  521. }
  522. }
  523. void
  524. GlobOpt::PreparePrepassFieldHoisting(Loop * loop)
  525. {
  526. BVSparse<JitArenaAllocator> * fieldHoistCandidates = loop->fieldHoistCandidates;
  527. #if DBG_DUMP
  528. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  529. {
  530. Output::Print(_u("\nFieldHoist: Start Loop: "));
  531. loop->GetHeadBlock()->DumpHeader();
  532. Output::Print(_u("FieldHoist: Backward candidates : "));
  533. fieldHoistCandidates->Dump();
  534. }
  535. #endif
  536. #if ENABLE_DEBUG_CONFIG_OPTIONS
  537. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  538. {
  539. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  540. Output::Print(_u("FieldHoist: START LOOP function %s (%s)\n"), this->func->GetJITFunctionBody()->GetDisplayName(), this->func->GetDebugNumberSet(debugStringBuffer));
  541. }
  542. #endif
  543. loop->fieldHoistCandidateTypes = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  544. if (fieldHoistCandidates->IsEmpty())
  545. {
  546. return;
  547. }
  548. BasicBlock * landingPad = loop->landingPad;
  549. // If it is live, the field doesn't need to be hoisted
  550. Assert(loop->liveInFieldHoistCandidates == nullptr);
  551. BVSparse<JitArenaAllocator> * liveInFieldHoistCandidates = fieldHoistCandidates->AndNew(landingPad->globOptData.liveFields);
  552. loop->liveInFieldHoistCandidates = liveInFieldHoistCandidates;
  553. if (!liveInFieldHoistCandidates->IsEmpty())
  554. {
  555. // Assume the live fields don't need to hoist for now
  556. fieldHoistCandidates->Minus(liveInFieldHoistCandidates);
  557. // If it was hoisted in an outer loop, and the value is live coming in, we don't need to hoist it again
  558. Loop * currentLoop = loop->parent;
  559. while (currentLoop != nullptr && this->DoFieldHoisting(currentLoop))
  560. {
  561. if (currentLoop->hoistedFields)
  562. {
  563. liveInFieldHoistCandidates->Minus(currentLoop->hoistedFields);
  564. }
  565. currentLoop = currentLoop->parent;
  566. }
  567. FOREACH_BITSET_IN_SPARSEBV(index, liveInFieldHoistCandidates)
  568. {
  569. if (this->FindValueFromHashTable(landingPad->globOptData.symToValueMap, index) == nullptr)
  570. {
  571. // Create initial values if we don't have one already for live fields
  572. Value * newValue = this->NewGenericValue(ValueType::Uninitialized);
  573. Value * oldValue = CopyValue(newValue, newValue->GetValueNumber());
  574. Sym *sym = this->func->m_symTable->Find(index);
  575. this->SetValue(&landingPad->globOptData, oldValue, sym);
  576. this->SetValue(&this->blockData, newValue, sym);
  577. }
  578. }
  579. NEXT_BITSET_IN_SPARSEBV;
  580. }
  581. // Assume that the candidates are hoisted on prepass
  582. landingPad->globOptData.liveFields->Or(fieldHoistCandidates);
  583. this->blockData.liveFields->Or(fieldHoistCandidates);
  584. Loop * parentLoop = loop->parent;
  585. FOREACH_BITSET_IN_SPARSEBV(index, fieldHoistCandidates)
  586. {
  587. // Create initial values
  588. Value * newValue = this->NewGenericValue(ValueType::Uninitialized);
  589. Value * oldValue = CopyValue(newValue, newValue->GetValueNumber());
  590. Sym *sym = this->func->m_symTable->Find(index);
  591. this->SetValue(&landingPad->globOptData, oldValue, sym);
  592. this->SetValue(&this->blockData, newValue, sym);
  593. StackSym* objectSym = sym->AsPropertySym()->m_stackSym;
  594. if (objectSym->HasObjectTypeSym())
  595. {
  596. StackSym* typeSym = objectSym->GetObjectTypeSym();
  597. // If the type isn't live into the loop, let's keep track of it, so we can add it to
  598. // live fields on pre-pass, verify if it is invariant through the loop, and if so produce it
  599. // into the loop on the real pass.
  600. if (!loop->landingPad->globOptData.liveFields->Test(typeSym->m_id))
  601. {
  602. Assert(!this->blockData.liveFields->Test(typeSym->m_id));
  603. loop->fieldHoistCandidateTypes->Set(typeSym->m_id);
  604. // Set object type live on prepass so we can track if it got killed in the loop. (see FinishOptHoistedPropOps)
  605. JsTypeValueInfo* typeValueInfo = JsTypeValueInfo::New(this->alloc, nullptr, nullptr);
  606. typeValueInfo->SetIsShared();
  607. this->SetSymStoreDirect(typeValueInfo, typeSym);
  608. ValueNumber typeValueNumber = this->NewValueNumber();
  609. Value* landingPadTypeValue = NewValue(typeValueNumber, typeValueInfo);
  610. Value* headerTypeValue = NewValue(typeValueNumber, typeValueInfo);
  611. SetObjectTypeFromTypeSym(typeSym, landingPadTypeValue, landingPad);
  612. SetObjectTypeFromTypeSym(typeSym, headerTypeValue, this->currentBlock);
  613. }
  614. }
  615. // If the sym holding the hoisted value is used as an instance pointer in the outer loop,
  616. // its type may appear to be live in the inner loop. But the instance itself is being killed
  617. // here, so make sure the type is killed as well.
  618. if (parentLoop != nullptr)
  619. {
  620. StackSym * copySym;
  621. Loop * hoistedLoop = FindFieldHoistStackSym(parentLoop, index, &copySym, nullptr);
  622. if (hoistedLoop != nullptr)
  623. {
  624. this->KillObjectType(copySym);
  625. }
  626. }
  627. }
  628. NEXT_BITSET_IN_SPARSEBV;
  629. Assert(this->TrackHoistableFields());
  630. // Initialize the bit vector to keep track of whether the hoisted value will reach a field load
  631. // to determine whether it should be hoisted.
  632. if (this->blockData.hoistableFields)
  633. {
  634. this->blockData.hoistableFields->Copy(fieldHoistCandidates);
  635. }
  636. else
  637. {
  638. this->blockData.hoistableFields = fieldHoistCandidates->CopyNew(this->alloc);
  639. this->currentBlock->globOptData.hoistableFields = this->blockData.hoistableFields;
  640. }
  641. this->blockData.hoistableFields->Or(liveInFieldHoistCandidates);
  642. #if DBG_DUMP
  643. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  644. {
  645. Output::Print(_u("FieldHoist: Prepass candidates (not live): "));
  646. fieldHoistCandidates->Dump();
  647. Output::Print(_u("FieldHoist: Prepass candidates (live) : "));
  648. liveInFieldHoistCandidates->Dump();
  649. }
  650. #endif
  651. }
  652. void
  653. GlobOpt::PrepareFieldHoisting(Loop * loop)
  654. {
  655. Assert(!this->IsLoopPrePass());
  656. if (loop->parent != nullptr)
  657. {
  658. loop->hasHoistedFields = loop->parent->hasHoistedFields;
  659. }
  660. BVSparse<JitArenaAllocator> * fieldHoistCandidates = loop->fieldHoistCandidates;
  661. BVSparse<JitArenaAllocator> * liveInFieldHoistCandidates = loop->liveInFieldHoistCandidates;
  662. if (fieldHoistCandidates->IsEmpty() && (!liveInFieldHoistCandidates || liveInFieldHoistCandidates->IsEmpty()))
  663. {
  664. if (loop->hasHoistedFields)
  665. {
  666. loop->hoistedFieldCopySyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  667. AnalysisAssert(loop->parent && loop->parent->hasHoistedFields);
  668. loop->hoistedFieldCopySyms->Copy(loop->parent->hoistedFieldCopySyms);
  669. loop->regAlloc.liveOnBackEdgeSyms->Or(loop->hoistedFieldCopySyms);
  670. }
  671. return;
  672. }
  673. BasicBlock * landingPad = loop->landingPad;
  674. Assert(landingPad->globOptData.hoistableFields == nullptr);
  675. Assert(this->blockData.hoistableFields == nullptr);
  676. BVSparse<JitArenaAllocator>* fieldHoistCandidateTypes = loop->fieldHoistCandidateTypes;
  677. // Remove the live fields that are added during prepass
  678. landingPad->globOptData.liveFields->Minus(fieldHoistCandidates);
  679. landingPad->globOptData.liveFields->Minus(fieldHoistCandidateTypes);
  680. // After prepass, if the field is not loaded on the back edge then we shouldn't hoist it
  681. fieldHoistCandidates->And(this->blockData.liveFields);
  682. liveInFieldHoistCandidates->And(this->blockData.liveFields);
  683. fieldHoistCandidateTypes->And(this->blockData.liveFields);
  684. // Remove the live fields that were added during prepass
  685. this->blockData.liveFields->Minus(fieldHoistCandidates);
  686. this->blockData.liveFields->Minus(fieldHoistCandidateTypes);
  687. loop->hoistedFields = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  688. loop->hoistedFieldCopySyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  689. if (loop->parent && loop->parent->hasHoistedFields)
  690. {
  691. loop->hoistedFieldCopySyms->Copy(loop->parent->hoistedFieldCopySyms);
  692. }
  693. Func * loopTopFunc = loop->GetFunc();
  694. // We built the list in reverse order, i.e., by prepending to it. Reverse it now so
  695. // the hoisted instr's can be inserted in the correct order.
  696. loop->prepassFieldHoistInstrCandidates.Reverse();
  697. // Hoist the field load
  698. FOREACH_SLISTBASE_ENTRY(IR::Instr *, instr, &loop->prepassFieldHoistInstrCandidates)
  699. {
  700. // We should have removed all fields that are hoisted in outer loops already.
  701. #if DBG
  702. AssertCanCopyPropOrCSEFieldLoad(instr);
  703. #endif
  704. PropertySym * propertySym = instr->GetSrc1()->AsSymOpnd()->m_sym->AsPropertySym();
  705. SymID symId = propertySym->m_id;
  706. if (loop->fieldHoistSymMap.ContainsKey(symId))
  707. {
  708. // The field is already hoisted
  709. #if DBG
  710. StackSym * hoistedCopySym;
  711. Assert(loop == FindFieldHoistStackSym(loop, symId, &hoistedCopySym, instr));
  712. #endif
  713. continue;
  714. }
  715. Assert(GlobOpt::IsLive(propertySym->m_stackSym, landingPad));
  716. if (fieldHoistCandidates->Test(symId))
  717. {
  718. // Hoist non-live field in
  719. Value * oldValue = this->FindValueFromHashTable(landingPad->globOptData.symToValueMap, symId);
  720. Value * newValue = this->FindValueFromHashTable(this->blockData.symToValueMap, symId);
  721. HoistFieldLoad(propertySym, loop, instr, oldValue, newValue);
  722. continue;
  723. }
  724. if (!liveInFieldHoistCandidates->Test(symId))
  725. {
  726. // Not live in back edge; don't hoist field
  727. Assert(!this->blockData.liveFields->Test(symId));
  728. continue;
  729. }
  730. Assert(landingPad->globOptData.liveFields->Test(symId));
  731. Assert(this->blockData.liveFields->Test(symId));
  732. // If the value is live in, we shouldn't have a hoisted symbol already
  733. Assert(!this->IsHoistedPropertySym(symId, loop->parent));
  734. Value * oldValue = this->FindPropertyValue(landingPad->globOptData.symToValueMap, symId);
  735. AssertMsg(oldValue != nullptr, "We should have created an initial value for the field");
  736. ValueInfo *oldValueInfo = oldValue->GetValueInfo();
  737. Value * newValue = this->FindPropertyValue(this->blockData.symToValueMap, symId);
  738. // The value of the loop isn't invariant, we need to create a value to hold the field through the loop
  739. int32 oldIntConstantValue;
  740. if (oldValueInfo->TryGetIntConstantValue(&oldIntConstantValue))
  741. {
  742. // Generate the constant load
  743. IR::IntConstOpnd * intConstOpnd = IR::IntConstOpnd::New(oldIntConstantValue, TyInt32, loopTopFunc);
  744. this->HoistFieldLoadValue(loop, newValue, symId, Js::OpCode::LdC_A_I4, intConstOpnd);
  745. }
  746. else if (oldValueInfo->IsFloatConstant())
  747. {
  748. // Generate the constant load
  749. this->HoistFieldLoadValue(loop, newValue, symId,
  750. Js::OpCode::LdC_A_R8, IR::FloatConstOpnd::New(oldValueInfo->AsFloatConstant()->FloatValue(), TyFloat64, loopTopFunc));
  751. }
  752. else
  753. {
  754. // This should be looking at the landingPad's value
  755. Sym * copySym = this->GetCopyPropSym(landingPad, nullptr, oldValue);
  756. if (copySym != nullptr)
  757. {
  758. if (newValue && oldValue->GetValueNumber() == newValue->GetValueNumber())
  759. {
  760. // The value of the field is invariant through the loop.
  761. // Copy prop can deal with this so we don't need to do anything.
  762. continue;
  763. }
  764. StackSym * copyStackSym = copySym->AsStackSym();
  765. // Transfer from an old copy prop value
  766. IR::RegOpnd * srcOpnd = IR::RegOpnd::New(copyStackSym, TyVar, loopTopFunc);
  767. srcOpnd->SetIsJITOptimizedReg(true);
  768. this->HoistFieldLoadValue(loop, newValue, symId, Js::OpCode::Ld_A, srcOpnd);
  769. }
  770. else
  771. {
  772. // We don't have a copy sym, even though the field value is live, we can't copy prop.
  773. // Generate the field load instead.
  774. #if DBG
  775. landingPad->globOptData.liveFields->Clear(symId);
  776. this->blockData.liveFields->Clear(symId);
  777. liveInFieldHoistCandidates->Clear(symId);
  778. fieldHoistCandidates->Set(symId);
  779. #endif
  780. HoistNewFieldLoad(propertySym, loop, instr, oldValue, newValue);
  781. }
  782. }
  783. }
  784. NEXT_SLISTBASE_ENTRY;
  785. this->FinishOptHoistedPropOps(loop);
  786. JitAdelete(this->alloc, loop->fieldHoistCandidateTypes);
  787. fieldHoistCandidateTypes = nullptr;
  788. loop->fieldHoistCandidateTypes = nullptr;
  789. loop->regAlloc.liveOnBackEdgeSyms->Or(loop->hoistedFieldCopySyms);
  790. #if DBG || DBG_DUMP
  791. if (loop->hoistedFields->IsEmpty())
  792. {
  793. Assert(loop->fieldHoistSymMap.Count() == 0);
  794. liveInFieldHoistCandidates->ClearAll();
  795. }
  796. else
  797. {
  798. // Update liveInFieldHoistCandidates for assert in FindFieldHoistStackSym
  799. liveInFieldHoistCandidates->And(loop->hoistedFields);
  800. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  801. {
  802. Output::Print(_u("FieldHoist: All candidates: "));
  803. loop->hoistedFields->Dump();
  804. Output::Print(_u("FieldHoist: Live in candidates: "));
  805. liveInFieldHoistCandidates->Dump();
  806. }
  807. }
  808. #else
  809. JitAdelete(this->alloc, liveInFieldHoistCandidates);
  810. loop->liveInFieldHoistCandidates = nullptr;
  811. #endif
  812. JitAdelete(this->alloc, fieldHoistCandidates);
  813. loop->fieldHoistCandidates = nullptr;
  814. }
  815. void
  816. GlobOpt::CheckFieldHoistCandidate(IR::Instr * instr, PropertySym * sym)
  817. {
  818. // See if this field load is hoistable.
  819. // This load probably may have a store or kill before it.
  820. // We will hoist it in another path. Just copy prop the value from the field store.
  821. //
  822. // For example:
  823. // loop
  824. // {
  825. // if ()
  826. // {
  827. // o.i =
  828. // = o.i <= not hoistable (but can copy prop)
  829. // }
  830. // else
  831. // {
  832. // = o.i <= hoistable
  833. // }
  834. // }
  835. if (this->blockData.hoistableFields->TestAndClear(sym->m_id))
  836. {
  837. Assert(this->blockData.liveFields->Test(sym->m_id));
  838. // We're adding this instruction as a candidate for hoisting. If it gets hoisted, its jit-time inline
  839. // cache will be used to generate the type check and bailout at the top of the loop. After we bail out,
  840. // however, we may not go down the code path on which this instruction resides, and so the inline cache
  841. // will not turn polymorphic. If we then re-jit, we would hoist the same instruction again, and get
  842. // stuck in infinite bailout cycle. That's why we use BailOutRecord::polymorphicCacheIndex for hoisted
  843. // field loads to force the profile info for the right inline cache into polymorphic state.
  844. this->rootLoopPrePass->prepassFieldHoistInstrCandidates.Prepend(this->alloc, instr);
  845. #if DBG_DUMP
  846. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  847. {
  848. Output::Print(_u("FieldHoist: Prepass marked hoist load"));
  849. Output::SkipToColumn(30);
  850. Output::Print(_u(" : "));
  851. instr->Dump();
  852. }
  853. #endif
  854. }
  855. }
  856. void
  857. GlobOpt::FinishOptHoistedPropOps(Loop * loop)
  858. {
  859. // Set up hoisted fields for object type specialization.
  860. Assert(loop);
  861. // This extra check for parent loop was added as a fix for Windows 8 Bug 480217. The issue there might have affected
  862. // the original redundant type elimination, but does not cause problems for object type spec. With this check some
  863. // operations which were candidates for object type spec in the backward pass (where we only checked the current loop),
  864. // could unexpectedly not be candidates, anymore. This led to problems in the lowerer.
  865. // (Do this only if we're doing the optimization in the loop's parent, which is where we're inserting
  866. // the hoisted instruction.)
  867. //if (loop->parent && !DoFieldRefOpts(loop->parent))
  868. //{
  869. // return;
  870. //}
  871. bool doFieldRefOpts = DoFieldRefOpts(loop);
  872. bool forceFieldHoisting = PHASE_FORCE(Js::FieldHoistPhase, this->func);
  873. bool doForcedTypeChecksOnly = !doFieldRefOpts && forceFieldHoisting;
  874. if (!doFieldRefOpts && !forceFieldHoisting)
  875. {
  876. IR::Instr * instrEnd = loop->endDisableImplicitCall;
  877. if (instrEnd == nullptr)
  878. {
  879. return;
  880. }
  881. FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, loop->landingPad->GetFirstInstr(), instrEnd)
  882. {
  883. // LdMethodFromFlags must always have a type check and bailout. If we hoisted it as a result of
  884. // -force:fieldHoist, we will have to set the bailout here again, even if there are implicit calls
  885. // in the loop (and DoFieldRefOpts returns false). See Windows Blue Bugs 608503 and 610237.
  886. if (instr->m_opcode == Js::OpCode::LdMethodFromFlags)
  887. {
  888. instr = SetTypeCheckBailOut(instr->GetSrc1(), instr, loop->bailOutInfo);
  889. }
  890. }
  891. NEXT_INSTR_EDITING_IN_RANGE;
  892. return;
  893. }
  894. // Walk the implicit-call-disabled region in the loop header, creating PropertySymOpnd's and
  895. // tracking liveness of the type/slot-array syms.
  896. IR::Instr * instrEnd = loop->endDisableImplicitCall;
  897. if (instrEnd == nullptr)
  898. {
  899. return;
  900. }
  901. Assert(loop->bailOutInfo->bailOutInstr != nullptr);
  902. // Consider (ObjTypeSpec): Do we really need all this extra tracking of live fields on back edges, so as to
  903. // remove them from the live fields on the loop header? We already do this in MergeBlockData called from
  904. // MergePredBlocksValueMaps, which takes place just before we get here.
  905. // Build the set of fields that are live on all back edges.
  906. // Use this to limit the type symbols we make live into the loop. We made the types of the hoisted fields
  907. // live in the prepass, so if they're not live on a back edge, that means some path through the loop
  908. // kills them.
  909. BVSparse<JitArenaAllocator> *bvBackEdge = nullptr;
  910. FOREACH_PREDECESSOR_BLOCK(predBlock, loop->GetHeadBlock())
  911. {
  912. if (!loop->IsDescendentOrSelf(predBlock->loop))
  913. {
  914. // This is the edge that enters the loop - not interesting here.
  915. continue;
  916. }
  917. if (!bvBackEdge)
  918. {
  919. bvBackEdge = predBlock->globOptData.liveFields;
  920. }
  921. else
  922. {
  923. bvBackEdge = bvBackEdge->AndNew(predBlock->globOptData.liveFields, this->alloc);
  924. }
  925. }
  926. NEXT_PREDECESSOR_BLOCK;
  927. if (!doForcedTypeChecksOnly)
  928. {
  929. FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, loop->landingPad->GetFirstInstr(), instrEnd)
  930. {
  931. IR::Opnd *opnd = instr->GetSrc1();
  932. if (opnd && opnd->IsSymOpnd() && opnd->AsSymOpnd()->IsPropertySymOpnd())
  933. {
  934. bool isHoistedTypeValue = false;
  935. bool isTypeInvariant = false;
  936. if (opnd->AsPropertySymOpnd()->HasObjectTypeSym())
  937. {
  938. StackSym* typeSym = opnd->AsPropertySymOpnd()->GetObjectTypeSym();
  939. // We've cleared the live bits for types that are purely hoisted (not live into the loop),
  940. // so we can't use FindObjectTypeValue here.
  941. Value* landingPadValue = FindValueFromHashTable(loop->landingPad->globOptData.symToValueMap, typeSym->m_id);
  942. Value* headerValue = FindValueFromHashTable(loop->GetHeadBlock()->globOptData.symToValueMap, typeSym->m_id);
  943. isHoistedTypeValue = landingPadValue != nullptr && loop->fieldHoistCandidateTypes->Test(typeSym->m_id);
  944. isTypeInvariant = landingPadValue != nullptr && headerValue != nullptr && landingPadValue->GetValueNumber() == headerValue->GetValueNumber();
  945. }
  946. // Prepare the operand for object type specialization by creating a type sym for it, if not yet present
  947. // and marking it as candidate for specialization.
  948. PreparePropertySymOpndForTypeCheckSeq(opnd->AsPropertySymOpnd(), instr, loop);
  949. // Let's update the existing type value, if possible, to retain the value number created in pre-pass.
  950. bool changesTypeValue = false;
  951. FinishOptPropOp(instr, opnd->AsPropertySymOpnd(), loop->landingPad, /* updateExistingValue = */ isHoistedTypeValue, nullptr, &changesTypeValue);
  952. instr = SetTypeCheckBailOut(opnd, instr, loop->bailOutInfo);
  953. // If we changed the type's value in the landing pad we want to reflect this change in the header block as well,
  954. // but only if the type is invariant throughout the loop. Note that if the type was live into the loop and
  955. // live on all back edges, but not invariant, it will already be live in the header, but its value will be blank,
  956. // because we merge type values conservatively on loop back edges. (see MergeJsTypeValueInfo)
  957. // Consider (ObjTypeSpec): There are corner cases where we copy prop an object pointer into the newly hoisted instruction,
  958. // and that object doesn't have a type yet. We then create a type on the fly (see GenerateHoistFieldLoad and
  959. // CopyPropPropertySymObj), and don't have a value for it in the landing pad. Thus we can't prove that the type is invariant
  960. // throughout the loop, and so we won't produce a value for it into the loop. This could be addressed by creating
  961. // a mapping of type syms from before to after object pointer copy prop.
  962. if (changesTypeValue && isTypeInvariant)
  963. {
  964. Assert(opnd->AsPropertySymOpnd()->HasObjectTypeSym());
  965. StackSym* typeSym = opnd->AsPropertySymOpnd()->GetObjectTypeSym();
  966. // If we changed the type value in the landing pad, we must have set it live there.
  967. Value* landingPadValue = FindObjectTypeValue(typeSym->m_id, loop->landingPad);
  968. Assert(landingPadValue != nullptr && landingPadValue->GetValueInfo()->IsJsType());
  969. // But in the loop header we may have only a value with the live bit still cleared,
  970. // so we can't use FindObjectTypeValue here.
  971. Value* headerValue = FindValueFromHashTable(loop->GetHeadBlock()->globOptData.symToValueMap, typeSym->m_id);
  972. Assert(headerValue != nullptr && headerValue->GetValueInfo()->IsJsType());
  973. Assert(!isHoistedTypeValue || landingPadValue->GetValueNumber() == headerValue->GetValueNumber());
  974. JsTypeValueInfo* valueInfo = landingPadValue->GetValueInfo()->AsJsType();
  975. valueInfo->SetIsShared();
  976. headerValue->SetValueInfo(valueInfo);
  977. loop->GetHeadBlock()->globOptData.liveFields->Set(typeSym->m_id);
  978. }
  979. #if DBG
  980. if (opnd->AsPropertySymOpnd()->HasObjectTypeSym())
  981. {
  982. StackSym* typeSym = opnd->AsPropertySymOpnd()->GetObjectTypeSym();
  983. Assert(!isHoistedTypeValue || isTypeInvariant || !loop->GetHeadBlock()->globOptData.liveFields->Test(typeSym->m_id));
  984. }
  985. #endif
  986. }
  987. }
  988. NEXT_INSTR_EDITING_IN_RANGE;
  989. }
  990. else
  991. {
  992. FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, loop->landingPad->GetFirstInstr(), instrEnd)
  993. {
  994. // LdMethodFromFlags must always have a type check and bailout. If we hoisted it as a result of
  995. // -force:fieldHoist, we will have to set the bailout here again, even if there are implicit calls
  996. // in the loop.
  997. if (instr->m_opcode == Js::OpCode::LdMethodFromFlags)
  998. {
  999. instr = SetTypeCheckBailOut(instr->GetSrc1(), instr, loop->bailOutInfo);
  1000. }
  1001. }
  1002. NEXT_INSTR_EDITING_IN_RANGE;
  1003. }
  1004. if (bvBackEdge)
  1005. {
  1006. // Take the fields not live on some back edge out of the set that's live into the loop.
  1007. this->blockData.liveFields->And(bvBackEdge);
  1008. }
  1009. }
  1010. void
  1011. GlobOpt::HoistFieldLoadValue(Loop * loop, Value * newValue, SymID symId, Js::OpCode opcode, IR::Opnd * srcOpnd)
  1012. {
  1013. IR::Instr * insertInstr = this->EnsureDisableImplicitCallRegion(loop);
  1014. Assert(!this->IsLoopPrePass());
  1015. Assert(IsPropertySymId(symId));
  1016. Assert(!loop->fieldHoistCandidates->Test(symId));
  1017. Assert(loop->landingPad->globOptData.liveFields->Test(symId));
  1018. Assert(this->blockData.liveFields->Test(symId));
  1019. Func * loopTopFunc = loop->GetFunc();
  1020. // Just transfer the copy prop sym to a new stack sym for the property.
  1021. // Consider: What happens if the outer loop already has a field hoist stack sym for this propertysym?
  1022. StackSym * newStackSym = StackSym::New(TyVar, loopTopFunc);
  1023. // This new stack sym may or may not be single def.
  1024. // Just make it not a single def so that we don't lose the value when it become non-single def.
  1025. newStackSym->m_isSingleDef = false;
  1026. IR::RegOpnd * newOpnd = IR::RegOpnd::New(newStackSym, TyVar, loopTopFunc);
  1027. IR::Instr * newInstr = IR::Instr::New(opcode, newOpnd, srcOpnd, loopTopFunc);
  1028. insertInstr->InsertBefore(newInstr);
  1029. loop->landingPad->globOptData.liveVarSyms->Set(newStackSym->m_id);
  1030. loop->varSymsOnEntry->Set(newStackSym->m_id);
  1031. // Update value in the current block
  1032. if (newValue == nullptr)
  1033. {
  1034. // Even though we don't use the symStore to copy prop the hoisted stack sym in the loop
  1035. // we might be able to propagate it out of the loop. Create a value just in case.
  1036. newValue = this->NewGenericValue(ValueType::Uninitialized, newStackSym);
  1037. // This should pass the sym directly.
  1038. Sym *sym = this->func->m_symTable->Find(symId);
  1039. this->SetValue(&this->blockData, newValue, sym);
  1040. Assert(newValue->GetValueInfo()->GetSymStore() == newStackSym);
  1041. }
  1042. else
  1043. {
  1044. this->SetValue(&this->blockData, newValue, newStackSym);
  1045. this->SetSymStoreDirect(newValue->GetValueInfo(), newStackSym);
  1046. }
  1047. this->blockData.liveVarSyms->Set(newStackSym->m_id);
  1048. loop->fieldHoistSymMap.Add(symId, newStackSym);
  1049. loop->hoistedFieldCopySyms->Set(newStackSym->m_id);
  1050. loop->hasHoistedFields = true;
  1051. loop->hoistedFields->Set(symId);
  1052. if(newInstr->GetSrc1()->IsRegOpnd())
  1053. {
  1054. // Make sure the source sym is available as a var
  1055. const auto srcRegOpnd = newInstr->GetSrc1()->AsRegOpnd();
  1056. if(!loop->landingPad->globOptData.liveVarSyms->Test(srcRegOpnd->m_sym->m_id))
  1057. {
  1058. this->ToVar(newInstr, srcRegOpnd, loop->landingPad, nullptr, false);
  1059. }
  1060. }
  1061. #if DBG_DUMP
  1062. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1063. {
  1064. Output::Print(_u("FieldHoist: Live value load "));
  1065. this->func->m_symTable->Find(symId)->Dump();
  1066. Output::SkipToColumn(30);
  1067. Output::Print(_u(" : "));
  1068. newInstr->Dump();
  1069. }
  1070. #endif
  1071. }
  1072. bool
  1073. GlobOpt::IsHoistablePropertySym(SymID symId) const
  1074. {
  1075. return this->blockData.hoistableFields && this->blockData.hoistableFields->Test(symId);
  1076. }
  1077. bool
  1078. GlobOpt::HasHoistableFields(BasicBlock * basicBlock)
  1079. {
  1080. return HasHoistableFields(&basicBlock->globOptData);
  1081. }
  1082. bool
  1083. GlobOpt::HasHoistableFields(GlobOptBlockData const * globOptData)
  1084. {
  1085. return globOptData->hoistableFields && !globOptData->hoistableFields->IsEmpty();
  1086. }
  1087. Loop *
  1088. GlobOpt::FindFieldHoistStackSym(Loop * startLoop, SymID propertySymId, StackSym ** copySym, IR::Instr * instrToHoist) const
  1089. {
  1090. Assert(IsPropertySymId(propertySymId));
  1091. if (instrToHoist && instrToHoist->m_opcode == Js::OpCode::LdMethodFromFlags)
  1092. {
  1093. return nullptr;
  1094. }
  1095. Loop * loop = startLoop;
  1096. while (loop && this->DoFieldHoisting(loop))
  1097. {
  1098. if (loop->fieldHoistSymMap.TryGetValue(propertySymId, copySym))
  1099. {
  1100. Assert(loop->hasHoistedFields);
  1101. Assert(loop->hoistedFields->Test(propertySymId));
  1102. if (this->IsLoopPrePass())
  1103. {
  1104. return loop;
  1105. }
  1106. BasicBlock * landingPad = loop->landingPad;
  1107. #if DBG
  1108. BOOL liveInSym = FALSE;
  1109. liveInSym = loop->liveInFieldHoistCandidates->Test(propertySymId);
  1110. Assert(landingPad->globOptData.liveFields->Test(propertySymId));
  1111. Assert(landingPad->globOptData.liveVarSyms->Test((*copySym)->m_id));
  1112. #endif
  1113. // This has been hoisted already.
  1114. // Verify the hoisted instruction.
  1115. bool found = false;
  1116. FOREACH_INSTR_BACKWARD_IN_BLOCK(instr, landingPad)
  1117. {
  1118. IR::Opnd * dstOpnd = instr->GetDst();
  1119. if (dstOpnd && dstOpnd->IsRegOpnd() && dstOpnd->AsRegOpnd()->m_sym == *copySym)
  1120. {
  1121. found = true;
  1122. #if DBG
  1123. // We used to try to assert that the property sym on the instruction in the landing pad
  1124. // matched the one on the instruction we're changing now. But we may have done object ptr
  1125. // copy prop in the landing pad, so the assertion no longer holds.
  1126. if (liveInSym)
  1127. {
  1128. Assert((instr->m_opcode == Js::OpCode::Ld_A && instr->GetSrc1()->IsRegOpnd())
  1129. || (instr->m_opcode == Js::OpCode::LdC_A_I4 && instr->GetSrc1()->IsIntConstOpnd())
  1130. || instr->m_opcode == Js::OpCode::LdC_A_R8 && instr->GetSrc1()->IsFloatConstOpnd());
  1131. }
  1132. else if (instrToHoist)
  1133. {
  1134. bool instrIsLdFldEquivalent = (instr->m_opcode == Js::OpCode::LdFld || instr->m_opcode == Js::OpCode::LdFldForCallApplyTarget);
  1135. bool instrToHoistIsLdFldEquivalent = (instrToHoist->m_opcode == Js::OpCode::LdFld || instrToHoist->m_opcode == Js::OpCode::LdFldForCallApplyTarget);
  1136. Assert(instr->m_opcode == instrToHoist->m_opcode ||
  1137. instrIsLdFldEquivalent && instrToHoistIsLdFldEquivalent ||
  1138. instr->m_opcode == Js::OpCode::LdMethodFld ||
  1139. instr->m_opcode == Js::OpCode::LdRootMethodFld ||
  1140. instr->m_opcode == Js::OpCode::ScopedLdMethodFld ||
  1141. instrToHoist->m_opcode == Js::OpCode::LdMethodFld ||
  1142. instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld ||
  1143. instrToHoist->m_opcode == Js::OpCode::ScopedLdMethodFld ||
  1144. (instrIsLdFldEquivalent && instrToHoist->m_opcode == Js::OpCode::LdRootFld) ||
  1145. (instr->m_opcode == Js::OpCode::LdMethodFld && instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld) ||
  1146. (instrToHoistIsLdFldEquivalent && instr->m_opcode == Js::OpCode::LdRootFld) ||
  1147. (instrToHoist->m_opcode == Js::OpCode::LdMethodFld && instr->m_opcode == Js::OpCode::LdRootMethodFld));
  1148. }
  1149. #endif
  1150. if (instrToHoist
  1151. && (instrToHoist->m_opcode == Js::OpCode::LdMethodFld ||
  1152. instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld ||
  1153. instrToHoist->m_opcode == Js::OpCode::ScopedLdMethodFld)
  1154. && instr->m_opcode != Js::OpCode::Ld_A
  1155. && instr->m_opcode != Js::OpCode::LdC_A_I4
  1156. && instr->m_opcode != Js::OpCode::LdC_A_R8)
  1157. {
  1158. // We may have property sym referred to by both Ld[Root]Fld and Ld[Root]MethodFld
  1159. // in the loop. If this happens, make sure the hoisted instruction is Ld[Root]MethodFld
  1160. // so we get the prototype inline cache fast path we want.
  1161. // Other differences such as error messages and HostDispatch behavior shouldn't
  1162. // matter, because we'll bail out in those cases.
  1163. Assert(instr->GetSrc1()->IsSymOpnd() && instr->GetSrc1()->AsSymOpnd()->m_sym->IsPropertySym());
  1164. instr->m_opcode = instrToHoist->m_opcode;
  1165. }
  1166. else if (instrToHoist &&
  1167. ((instr->m_opcode == Js::OpCode::LdFld && instrToHoist->m_opcode == Js::OpCode::LdRootFld)
  1168. || (instr->m_opcode == Js::OpCode::LdMethodFld && instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld)))
  1169. {
  1170. instr->m_opcode = instrToHoist->m_opcode;
  1171. }
  1172. break;
  1173. }
  1174. }
  1175. NEXT_INSTR_BACKWARD_IN_BLOCK;
  1176. Assert(found);
  1177. return loop;
  1178. }
  1179. Assert(!loop->hoistedFields || !loop->hoistedFields->Test(propertySymId));
  1180. loop = loop->parent;
  1181. }
  1182. return nullptr;
  1183. }
  1184. void
  1185. GlobOpt::HoistFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, Value * oldValue, Value * newValue)
  1186. {
  1187. Loop * parentLoop = loop->parent;
  1188. if (parentLoop != nullptr)
  1189. {
  1190. StackSym * copySym;
  1191. Loop * hoistedLoop = FindFieldHoistStackSym(parentLoop, sym->m_id, &copySym, instr);
  1192. if (hoistedLoop != nullptr)
  1193. {
  1194. // Use an outer loop pre-assigned stack sym if it is already hoisted there
  1195. Assert(hoistedLoop != loop);
  1196. GenerateHoistFieldLoad(sym, loop, instr, copySym, oldValue, newValue);
  1197. return;
  1198. }
  1199. }
  1200. HoistNewFieldLoad(sym, loop, instr, oldValue, newValue);
  1201. }
  1202. void
  1203. GlobOpt::HoistNewFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, Value * oldValue, Value * newValue)
  1204. {
  1205. Assert(!this->IsHoistedPropertySym(sym->m_id, loop));
  1206. StackSym * newStackSym = StackSym::New(TyVar, this->func);
  1207. // This new stack sym may or may not be single def.
  1208. // Just make it not a single def so that we don't lose the value when it become non-single def.
  1209. newStackSym->m_isSingleDef = false;
  1210. GenerateHoistFieldLoad(sym, loop, instr, newStackSym, oldValue, newValue);
  1211. }
  1212. void
  1213. GlobOpt::GenerateHoistFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, StackSym * newStackSym, Value * oldValue, Value * newValue)
  1214. {
  1215. Assert(loop != nullptr);
  1216. SymID symId = sym->m_id;
  1217. BasicBlock * landingPad = loop->landingPad;
  1218. #if DBG
  1219. Assert(!this->IsLoopPrePass());
  1220. AssertCanCopyPropOrCSEFieldLoad(instr);
  1221. Assert(instr->GetSrc1()->AsSymOpnd()->m_sym == sym);
  1222. Assert(loop->fieldHoistCandidates->Test(symId));
  1223. Assert(!landingPad->globOptData.liveFields->Test(sym->m_id));
  1224. Assert(!this->blockData.liveFields->Test(sym->m_id));
  1225. Assert(!loop->fieldHoistSymMap.ContainsKey(symId));
  1226. #endif
  1227. loop->fieldHoistSymMap.Add(symId, newStackSym);
  1228. loop->hoistedFieldCopySyms->Set(newStackSym->m_id);
  1229. Func * loopTopFunc = loop->GetFunc();
  1230. // Generate the hoisted field load
  1231. IR::RegOpnd * newDst = IR::RegOpnd::New(newStackSym, TyVar, loopTopFunc);
  1232. IR::SymOpnd * newSrc;
  1233. if (instr->GetSrc1() && instr->GetSrc1()->IsSymOpnd() && instr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd())
  1234. {
  1235. IR::PropertySymOpnd * srcPropertySymOpnd = instr->GetSrc1()->AsPropertySymOpnd();
  1236. AssertMsg(!srcPropertySymOpnd->IsTypeAvailable() && !srcPropertySymOpnd->IsTypeChecked() && !srcPropertySymOpnd->IsWriteGuardChecked(),
  1237. "Why are the object type spec bits set before we specialized this instruction?");
  1238. // We only set guarded properties in the dead store pass, so they shouldn't be set here yet. If they were
  1239. // we would need to move them from this operand to the operand which is being copy propagated.
  1240. Assert(srcPropertySymOpnd->GetGuardedPropOps() == nullptr);
  1241. // We're hoisting an instruction from the loop, so we're placing it in a different position in the flow. Make sure only the flow
  1242. // insensitive info is copied.
  1243. IR::PropertySymOpnd * newPropertySymOpnd = srcPropertySymOpnd->CopyWithoutFlowSensitiveInfo(loopTopFunc);
  1244. Assert(newPropertySymOpnd->GetObjTypeSpecFlags() == 0);
  1245. Value *const propertyOwnerValueInLandingPad =
  1246. FindValue(loop->landingPad->globOptData.symToValueMap, srcPropertySymOpnd->GetObjectSym());
  1247. if(propertyOwnerValueInLandingPad)
  1248. {
  1249. newPropertySymOpnd->SetPropertyOwnerValueType(propertyOwnerValueInLandingPad->GetValueInfo()->Type());
  1250. }
  1251. newSrc = newPropertySymOpnd;
  1252. }
  1253. else
  1254. {
  1255. newSrc = IR::SymOpnd::New(sym, TyVar, func);
  1256. }
  1257. IR::Instr * newInstr = nullptr;
  1258. ValueType profiledFieldType;
  1259. if (instr->IsProfiledInstr())
  1260. {
  1261. profiledFieldType = instr->AsProfiledInstr()->u.FldInfo().valueType;
  1262. }
  1263. newInstr = IR::Instr::New(instr->m_opcode, newDst, newSrc, loopTopFunc);
  1264. // Win8 910551: Kill the live field for this hoisted field load
  1265. KillLiveFields(newStackSym, this->blockData.liveFields);
  1266. IR::Instr * insertInstr = this->EnsureDisableImplicitCallRegion(loop);
  1267. insertInstr->InsertBefore(newInstr);
  1268. // Track use/def of arguments object
  1269. this->OptArguments(newInstr);
  1270. landingPad->globOptData.liveFields->Set(symId);
  1271. this->blockData.liveFields->Set(symId);
  1272. // If we are reusing an already hoisted stack sym, while the var version is made live, we need to make sure that specialized
  1273. // versions of it are not live since this is effectively a field reload.
  1274. this->ToVarStackSym(newStackSym, landingPad);
  1275. this->ToVarStackSym(newStackSym, this->currentBlock);
  1276. loop->varSymsOnEntry->Set(newStackSym->m_id);
  1277. loop->int32SymsOnEntry->Clear(newStackSym->m_id);
  1278. loop->lossyInt32SymsOnEntry->Clear(newStackSym->m_id);
  1279. loop->float64SymsOnEntry->Clear(newStackSym->m_id);
  1280. Assert(oldValue != nullptr);
  1281. // Create a value in case we can copy prop out of the loop
  1282. if (newValue == nullptr || newValue->GetValueInfo()->IsUninitialized())
  1283. {
  1284. const bool hoistValue = newValue && oldValue->GetValueNumber() == newValue->GetValueNumber();
  1285. if(newValue)
  1286. {
  1287. // Assuming the profile data gives more precise value types based on the path it took at runtime, we can improve the
  1288. // original value type.
  1289. newValue->GetValueInfo()->Type() = profiledFieldType;
  1290. }
  1291. else
  1292. {
  1293. newValue = NewGenericValue(profiledFieldType, newDst);
  1294. }
  1295. this->SetValue(&this->blockData, newValue, sym);
  1296. if(hoistValue)
  1297. {
  1298. // The field value is invariant through the loop. Since we're updating its value to a more precise value, hoist the
  1299. // new value up to the loop landing pad where the field is being hoisted.
  1300. Assert(loop == currentBlock->loop);
  1301. Assert(landingPad == loop->landingPad);
  1302. oldValue = CopyValue(newValue, newValue->GetValueNumber());
  1303. SetValue(&landingPad->globOptData, oldValue, sym);
  1304. }
  1305. }
  1306. newInstr->GetDst()->SetValueType(oldValue->GetValueInfo()->Type());
  1307. newInstr->GetSrc1()->SetValueType(oldValue->GetValueInfo()->Type());
  1308. this->SetValue(&loop->landingPad->globOptData, oldValue, newStackSym);
  1309. this->SetValue(&this->blockData, newValue, newStackSym);
  1310. instr->GetSrc1()->SetValueType(newValue->GetValueInfo()->Type());
  1311. loop->hasHoistedFields = true;
  1312. loop->hoistedFields->Set(sym->m_id);
  1313. // Try to do object pointer copy prop. Do it now because, for instance, we want the ToVar we insert below
  1314. // to define the right sym (Win8 906875).
  1315. // Consider: Restructure field hoisting to call OptBlock on the completed loop landing pad instead of
  1316. // doing these optimizations and bitvector updates piecemeal.
  1317. #ifdef DBG
  1318. PropertySym *propertySymUseBefore = nullptr;
  1319. Assert(this->byteCodeUses == nullptr);
  1320. this->byteCodeUsesBeforeOpt->ClearAll();
  1321. GlobOpt::TrackByteCodeSymUsed(instr, this->byteCodeUsesBeforeOpt, &propertySymUseBefore);
  1322. #endif
  1323. this->CaptureByteCodeSymUses(newInstr);
  1324. // Consider (ObjTypeSpec): If we copy prop an object sym into the hoisted instruction we lose track of the original
  1325. // object sym's type being invariant through the loop and so we won't produce the new type's value into the loop,
  1326. // and end up with unnecessary type checks in the loop. If the new type isn't live in the landing pad (that is
  1327. // we weren't tracking its liveness and invariance through the loop), but the old type was invariant, let's add
  1328. // the new type to fieldHoistCandidateTypes and produce a value for it in the landing pad and loop header. If the
  1329. // old type was live then its liveness and invariance are already correctly reflected and there is nothing to do.
  1330. this->CopyPropPropertySymObj(newSrc, newInstr);
  1331. if (this->byteCodeUses != nullptr)
  1332. {
  1333. sym = newSrc->m_sym->AsPropertySym();
  1334. this->InsertByteCodeUses(newInstr);
  1335. }
  1336. StackSym * propertyBase = sym->m_stackSym;
  1337. if (!landingPad->globOptData.liveVarSyms->Test(propertyBase->m_id))
  1338. {
  1339. IR::RegOpnd *newOpnd = IR::RegOpnd::New(propertyBase, TyVar, instr->m_func);
  1340. this->ToVar(newInstr, newOpnd, landingPad, this->FindValue(propertyBase), false);
  1341. }
  1342. if (landingPad->globOptData.canStoreTempObjectSyms && landingPad->globOptData.canStoreTempObjectSyms->Test(propertyBase->m_id))
  1343. {
  1344. newSrc->SetCanStoreTemp();
  1345. }
  1346. #if DBG_DUMP
  1347. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1348. {
  1349. Output::Print(_u("FieldHoist: Hoisted Load "));
  1350. Output::SkipToColumn(30);
  1351. Output::Print(_u(" : "));
  1352. newInstr->Dump();
  1353. }
  1354. #endif
  1355. #if ENABLE_DEBUG_CONFIG_OPTIONS
  1356. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1357. {
  1358. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  1359. Output::Print(_u(" FieldHoist: function %s (%s) "), this->func->GetJITFunctionBody()->GetDisplayName(), this->func->GetDebugNumberSet(debugStringBuffer));
  1360. newInstr->DumpTestTrace();
  1361. }
  1362. #endif
  1363. }
  1364. Value *
  1365. GlobOpt::CreateFieldSrcValue(PropertySym * sym, PropertySym * originalSym, IR::Opnd ** ppOpnd, IR::Instr * instr)
  1366. {
  1367. #if DBG
  1368. // If the opcode going to kill all field values immediate anyway, we shouldn't be giving it a value
  1369. Assert(!instr->UsesAllFields());
  1370. AssertCanCopyPropOrCSEFieldLoad(instr);
  1371. Assert(instr->GetSrc1() == *ppOpnd);
  1372. #endif
  1373. // Only give a value to fields if we are doing field copy prop.
  1374. // Consider: We should always copy prop local slots, but the only use right now is LdSlot from jit loop body.
  1375. // This should have one onus load, and thus no need for copy prop of field itself. We may want to support
  1376. // copy prop LdSlot if there are other uses of local slots
  1377. if (!this->DoFieldCopyProp())
  1378. {
  1379. return nullptr;
  1380. }
  1381. BOOL wasLive = this->blockData.liveFields->TestAndSet(sym->m_id);
  1382. if (this->DoFieldHoisting())
  1383. {
  1384. // We don't track copy prop sym for fields on loop prepass, no point in creating an empty unknown value.
  1385. // If we can copy prop through the back edge, we would have hoisted the field load, in which case we will
  1386. // just pick the live in copy prop sym for the field or create a new sym for the stack sym of the hoist field.
  1387. if (this->IsLoopPrePass())
  1388. {
  1389. // We don't clear the value when we kill the field.
  1390. // Clear it to make sure we don't use the old value.
  1391. this->blockData.symToValueMap->Clear(sym->m_id);
  1392. return nullptr;
  1393. }
  1394. }
  1395. else if (sym != originalSym)
  1396. {
  1397. this->blockData.liveFields->TestAndSet(originalSym->m_id);
  1398. }
  1399. if (!wasLive)
  1400. {
  1401. // We don't clear the value when we kill the field.
  1402. // Clear it to make sure we don't use the old value.
  1403. this->blockData.symToValueMap->Clear(sym->m_id);
  1404. this->blockData.symToValueMap->Clear(originalSym->m_id);
  1405. }
  1406. Assert((*ppOpnd)->AsSymOpnd()->m_sym == sym || this->IsLoopPrePass());
  1407. if (wasLive)
  1408. {
  1409. // We should have dealt with field hoist already
  1410. Assert(!IsHoistedPropertySym(sym) || instr->m_opcode == Js::OpCode::CheckFixedFld);
  1411. // We don't use the sym store to do copy prop on hoisted fields, but create a value
  1412. // in case it can be copy prop out of the loop.
  1413. }
  1414. else
  1415. {
  1416. // If it wasn't live, it should not be hoistable
  1417. Assert(!this->IsHoistablePropertySym(sym->m_id));
  1418. }
  1419. return this->NewGenericValue(ValueType::Uninitialized, *ppOpnd);
  1420. }
  1421. bool
  1422. GlobOpt::FieldHoistOptSrc(IR::Opnd *opnd, IR::Instr *instr, PropertySym * propertySym)
  1423. {
  1424. if (!DoFieldHoisting())
  1425. {
  1426. return false;
  1427. }
  1428. if (!GlobOpt::TransferSrcValue(instr) || instr->m_opcode == Js::OpCode::LdMethodFromFlags)
  1429. {
  1430. // Instructions like typeof don't transfer value of the field, we can't hoist those right now.
  1431. return false;
  1432. }
  1433. if (TrackHoistableFields() && HasHoistableFields(&this->blockData))
  1434. {
  1435. Assert(this->DoFieldHoisting());
  1436. CheckFieldHoistCandidate(instr, propertySym);
  1437. // This may have been a hoistable field with respect to the current loop. If so, that means:
  1438. // - It is assumed that it will be live on the back-edge and hence currently live for the purposes of determining
  1439. // whether to hoist the field.
  1440. // - It is not already hoisted outside a parent loop or not live coming into this loop.
  1441. // - It is not already marked for hoisting in this loop.
  1442. //
  1443. // If this is a hoistable field, and if the field is ultimately chosen to be hoisted outside this loop, the field will
  1444. // be reloaded in this loop's landing pad. However, since the field may already have been hoisted outside a parent
  1445. // loop with a specialized stack sym still live and a value still available (since these are killed lazily), neither of
  1446. // which are valid anymore due to the reload, we still need to kill the specialized stack syms and the field value. On
  1447. // the other hand, if this was not a hoistable field, we need to treat it as a field load anyway. So, since this is the
  1448. // first use of the field in this loop, fall through to reload the field.
  1449. }
  1450. else if (!this->IsLoopPrePass())
  1451. {
  1452. if (CopyPropHoistedFields(propertySym, &opnd, instr))
  1453. {
  1454. return true;
  1455. }
  1456. }
  1457. this->ReloadFieldHoistStackSym(instr, propertySym);
  1458. return false;
  1459. }
  1460. void
  1461. GlobOpt::FieldHoistOptDst(IR::Instr * instr, PropertySym * propertySym, Value * src1Val)
  1462. {
  1463. if(DoFieldHoisting())
  1464. {
  1465. switch (instr->m_opcode)
  1466. {
  1467. case Js::OpCode::StSlot:
  1468. case Js::OpCode::StSlotChkUndecl:
  1469. case Js::OpCode::StFld:
  1470. case Js::OpCode::StRootFld:
  1471. case Js::OpCode::StFldStrict:
  1472. case Js::OpCode::StRootFldStrict:
  1473. CopyStoreFieldHoistStackSym(instr, propertySym, src1Val);
  1474. break;
  1475. }
  1476. }
  1477. }
  1478. bool
  1479. GlobOpt::CopyPropHoistedFields(PropertySym * sym, IR::Opnd ** ppOpnd, IR::Instr * instr)
  1480. {
  1481. Assert(GlobOpt::TransferSrcValue(instr));
  1482. if (!this->blockData.liveFields->Test(sym->m_id))
  1483. {
  1484. // Not live
  1485. return false;
  1486. }
  1487. StackSym * hoistedCopySym;
  1488. Loop * loop = FindFieldHoistStackSym(this->currentBlock->loop, sym->m_id, &hoistedCopySym, instr);
  1489. Assert(loop != nullptr || !this->IsHoistablePropertySym(sym->m_id));
  1490. if (loop)
  1491. {
  1492. // The field was live before, so we have the hoisted stack sym live value, just copy prop it
  1493. *ppOpnd = CopyPropReplaceOpnd(instr, *ppOpnd, hoistedCopySym);
  1494. #if DBG
  1495. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1496. {
  1497. Output::Print(_u("FieldHoist: Copy prop "));
  1498. sym->Dump();
  1499. Output::SkipToColumn(30);
  1500. Output::Print(_u(" : "));
  1501. instr->Dump();
  1502. }
  1503. #endif
  1504. return true;
  1505. }
  1506. return false;
  1507. }
  1508. void
  1509. GlobOpt::ReloadFieldHoistStackSym(IR::Instr * instr, PropertySym * propertySym)
  1510. {
  1511. Assert(GlobOpt::TransferSrcValue(instr));
  1512. StackSym * fieldHoistSym;
  1513. Loop * loop = this->FindFieldHoistStackSym(this->currentBlock->loop, propertySym->m_id, &fieldHoistSym, instr);
  1514. if (loop == nullptr)
  1515. {
  1516. return;
  1517. }
  1518. // When a field is killed, ideally the specialized versions of the corresponding hoisted stack syms should also be killed,
  1519. // since the field needs to be reloaded the next time it's used (which may be earlier in the loop). However, killing the
  1520. // specialized stack syms when the field is killed requires discovering and walking all fields that are killed and their
  1521. // hoisted stack syms, which requires more computation (since many fields can be killed at once).
  1522. //
  1523. // Alternatively, we can kill the specialized stack syms for a field when the field is reloaded, which is what's happening
  1524. // here. Since this happens per field and lazily, it requires less work. It works because killing the specialized stack
  1525. // syms only matters when the field is reloaded.
  1526. //
  1527. // Furthermore, to handle the case where a field is not live on entry into the loop (field is killed in the loop and not
  1528. // reloaded in the same loop afterwards), the specialized stack syms for that field must also be killed on entry into the
  1529. // loop. Instead of checking all hoisted field stack syms on entry into a loop after the prepass merge, and killing them if
  1530. // their corresponding field is not live, this is also done in a lazy fashion as above, only when a field is reloaded. If a
  1531. // field is reloaded in a loop before it's killed, and not reloaded again after the kill, the field won't be live on entry,
  1532. // and hence the specialized stack syms should also not be live on entry. This is true for all parent loops up to the
  1533. // nearest parent loop out of which the field is hoisted.
  1534. ToVarStackSym(fieldHoistSym, currentBlock);
  1535. if(!this->IsLoopPrePass())
  1536. {
  1537. for(Loop *currentLoop = currentBlock->loop;
  1538. currentLoop != loop->parent && !currentLoop->liveFieldsOnEntry->Test(propertySym->m_id);
  1539. currentLoop = currentLoop->parent)
  1540. {
  1541. currentLoop->int32SymsOnEntry->Clear(fieldHoistSym->m_id);
  1542. currentLoop->lossyInt32SymsOnEntry->Clear(fieldHoistSym->m_id);
  1543. currentLoop->float64SymsOnEntry->Clear(fieldHoistSym->m_id);
  1544. }
  1545. }
  1546. // Win8 943662: Kill the live field for this hoisted field load
  1547. this->KillLiveFields(fieldHoistSym, this->blockData.liveFields);
  1548. if (this->IsLoopPrePass())
  1549. {
  1550. // In the prepass we are conservative and always assume that the fields are going to be reloaded
  1551. // because we don't loop until value is unchanged and we are unable to detect dependencies.
  1552. // Clear the value of the field to kill the value of the field even if it still live now.
  1553. this->blockData.liveFields->Clear(propertySym->m_id);
  1554. // If we have to reload, we don't know the value, kill the old value for the fieldHoistSym.
  1555. this->blockData.symToValueMap->Clear(fieldHoistSym->m_id);
  1556. // No IR transformations in the prepass.
  1557. return;
  1558. }
  1559. // If we are reloading, the field should be dead. CreateFieldSrc will create a value for the field.
  1560. Assert(!this->blockData.liveFields->Test(propertySym->m_id));
  1561. // Copy the dst to the field hoist sym.
  1562. IR::Instr * copyInstr = IR::Instr::New(Js::OpCode::Ld_A, IR::RegOpnd::New(fieldHoistSym, TyVar, instr->m_func), instr->GetDst(), instr->m_func);
  1563. instr->InsertAfter(copyInstr);
  1564. #if DBG_DUMP
  1565. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1566. {
  1567. Output::Print(_u("FieldHoist: Reload field sym "));
  1568. Output::SkipToColumn(30);
  1569. Output::Print(_u(" : "));
  1570. instr->Dump();
  1571. }
  1572. #endif
  1573. }
  1574. void
  1575. GlobOpt::CopyStoreFieldHoistStackSym(IR::Instr * storeFldInstr, PropertySym * sym, Value * src1Val)
  1576. {
  1577. // In the real (not prepass) pass, do the actual IR rewrites.
  1578. // In the prepass, only track the impact that the rewrites will have. (See Win8 521029)
  1579. Assert(storeFldInstr->m_opcode == Js::OpCode::StSlot
  1580. || storeFldInstr->m_opcode == Js::OpCode::StSlotChkUndecl
  1581. || storeFldInstr->m_opcode == Js::OpCode::StFld
  1582. || storeFldInstr->m_opcode == Js::OpCode::StRootFld
  1583. || storeFldInstr->m_opcode == Js::OpCode::StFldStrict
  1584. || storeFldInstr->m_opcode == Js::OpCode::StRootFldStrict);
  1585. Assert(storeFldInstr->GetDst()->GetType() == TyVar);
  1586. // We may use StSlot for all sort of things other then assigning TyVars
  1587. Assert(storeFldInstr->GetSrc1()->GetType() == TyVar || storeFldInstr->m_opcode == Js::OpCode::StSlot || storeFldInstr->m_opcode == Js::OpCode::StSlotChkUndecl);
  1588. Assert(storeFldInstr->GetSrc2() == nullptr);
  1589. StackSym * copySym;
  1590. Loop * loop = this->FindFieldHoistStackSym(this->currentBlock->loop, sym->m_id, &copySym);
  1591. if (loop == nullptr)
  1592. {
  1593. return;
  1594. }
  1595. IR::Opnd * srcOpnd = storeFldInstr->GetSrc1();
  1596. Func * storeFldFunc = storeFldInstr->m_func;
  1597. IR::Instr * newInstr;
  1598. if (!this->IsLoopPrePass())
  1599. {
  1600. this->CaptureByteCodeSymUses(storeFldInstr);
  1601. IR::RegOpnd * dstOpnd = IR::RegOpnd::New(copySym, TyVar, storeFldFunc);
  1602. dstOpnd->SetIsJITOptimizedReg(true);
  1603. storeFldInstr->UnlinkSrc1();
  1604. newInstr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, srcOpnd, storeFldFunc);
  1605. storeFldInstr->SetSrc1(dstOpnd);
  1606. storeFldInstr->InsertBefore(newInstr);
  1607. }
  1608. this->ToVarStackSym(copySym, this->currentBlock); // The field-hoisted stack sym is now unspecialized
  1609. Value * dstVal = this->CopyValue(src1Val);
  1610. TrackCopiedValueForKills(dstVal);
  1611. this->SetSymStoreDirect(dstVal->GetValueInfo(), copySym);
  1612. this->SetValue(&this->blockData, dstVal, copySym);
  1613. // Copy the type specialized sym as well, in case we have a use for them
  1614. bool neededCopySymDef = false;
  1615. if(srcOpnd->IsRegOpnd())
  1616. {
  1617. StackSym *const srcSym = srcOpnd->AsRegOpnd()->m_sym;
  1618. if (this->blockData.liveInt32Syms->Test(srcSym->m_id))
  1619. {
  1620. this->blockData.liveInt32Syms->Set(copySym->m_id);
  1621. if(this->blockData.liveLossyInt32Syms->Test(srcSym->m_id))
  1622. {
  1623. this->blockData.liveLossyInt32Syms->Set(copySym->m_id);
  1624. }
  1625. if (!this->IsLoopPrePass())
  1626. {
  1627. StackSym * int32CopySym = copySym->GetInt32EquivSym(storeFldFunc);
  1628. IR::RegOpnd * int32CopyOpnd = IR::RegOpnd::New(int32CopySym, TyInt32, storeFldFunc);
  1629. IR::RegOpnd * int32SrcOpnd = IR::RegOpnd::New(srcSym->GetInt32EquivSym(nullptr),
  1630. TyInt32, storeFldFunc);
  1631. newInstr = IR::Instr::New(Js::OpCode::Ld_I4, int32CopyOpnd, int32SrcOpnd, storeFldFunc);
  1632. int32SrcOpnd->SetIsJITOptimizedReg(true);
  1633. storeFldInstr->InsertBefore(newInstr);
  1634. }
  1635. neededCopySymDef = true;
  1636. }
  1637. if (this->blockData.liveFloat64Syms->Test(srcSym->m_id))
  1638. {
  1639. this->blockData.liveFloat64Syms->Set(copySym->m_id);
  1640. if (!this->IsLoopPrePass())
  1641. {
  1642. StackSym * float64CopySym = copySym->GetFloat64EquivSym(storeFldFunc);
  1643. IR::RegOpnd * float64CopyOpnd = IR::RegOpnd::New(float64CopySym, TyFloat64, storeFldFunc);
  1644. IR::RegOpnd * float64SrcOpnd = IR::RegOpnd::New(srcSym->GetFloat64EquivSym(nullptr),
  1645. TyFloat64, storeFldFunc);
  1646. newInstr = IR::Instr::New(Js::OpCode::Ld_A, float64CopyOpnd, float64SrcOpnd, storeFldFunc);
  1647. float64SrcOpnd->SetIsJITOptimizedReg(true);
  1648. storeFldInstr->InsertBefore(newInstr);
  1649. }
  1650. neededCopySymDef = true;
  1651. }
  1652. }
  1653. else if(srcOpnd->IsAddrOpnd())
  1654. {
  1655. const auto srcAddrOpnd = srcOpnd->AsAddrOpnd();
  1656. if(srcAddrOpnd->IsVar() && Js::TaggedInt::Is(srcAddrOpnd->m_address))
  1657. {
  1658. this->blockData.liveInt32Syms->Set(copySym->m_id);
  1659. if (!this->IsLoopPrePass())
  1660. {
  1661. StackSym * int32CopySym = copySym->GetInt32EquivSym(storeFldFunc);
  1662. IR::RegOpnd * int32CopyOpnd = IR::RegOpnd::New(int32CopySym, TyInt32, storeFldFunc);
  1663. IR::IntConstOpnd * int32SrcOpnd =
  1664. IR::IntConstOpnd::New(Js::TaggedInt::ToInt32(srcAddrOpnd->m_address), TyInt32, storeFldFunc);
  1665. newInstr = IR::Instr::New(Js::OpCode::Ld_I4, int32CopyOpnd, int32SrcOpnd, storeFldFunc);
  1666. int32SrcOpnd->SetIsJITOptimizedReg(true);
  1667. storeFldInstr->InsertBefore(newInstr);
  1668. }
  1669. neededCopySymDef = true;
  1670. }
  1671. }
  1672. if(IsLoopPrePass() && neededCopySymDef)
  1673. {
  1674. // Record the def that would have been added
  1675. rootLoopPrePass->symsDefInLoop->Set(copySym->m_id);
  1676. }
  1677. this->KillLiveFields(copySym, this->blockData.liveFields);
  1678. #if DBG_DUMP
  1679. if (!this->IsLoopPrePass())
  1680. {
  1681. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1682. {
  1683. Output::Print(_u("FieldHoist: Copy field store "));
  1684. Output::SkipToColumn(30);
  1685. Output::Print(_u(" : "));
  1686. storeFldInstr->Dump();
  1687. }
  1688. }
  1689. #endif
  1690. }
  1691. bool
  1692. GlobOpt::NeedBailOnImplicitCallWithFieldOpts(Loop *loop, bool hasLiveFields) const
  1693. {
  1694. if (!((this->DoFieldHoisting(loop) && loop->hasHoistedFields) ||
  1695. ((this->DoFieldRefOpts(loop) ||
  1696. this->DoFieldCopyProp(loop)) &&
  1697. hasLiveFields)))
  1698. {
  1699. return false;
  1700. }
  1701. return true;
  1702. }
  1703. IR::Instr *
  1704. GlobOpt::EnsureDisableImplicitCallRegion(Loop * loop)
  1705. {
  1706. Assert(loop->bailOutInfo != nullptr);
  1707. IR::Instr * endDisableImplicitCall = loop->endDisableImplicitCall;
  1708. if (endDisableImplicitCall)
  1709. {
  1710. return endDisableImplicitCall;
  1711. }
  1712. IR::Instr * bailOutTarget = EnsureBailTarget(loop);
  1713. Func * bailOutFunc = loop->GetFunc();
  1714. Assert(loop->bailOutInfo->bailOutFunc == bailOutFunc);
  1715. IR::MemRefOpnd * disableImplicitCallAddress = IR::MemRefOpnd::New(this->func->GetThreadContextInfo()->GetDisableImplicitFlagsAddr(), TyInt8, bailOutFunc);
  1716. IR::IntConstOpnd * disableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitCallAndExceptionFlag, TyInt8, bailOutFunc, true);
  1717. IR::IntConstOpnd * enableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitNoFlag, TyInt8, bailOutFunc, true);
  1718. IR::Opnd * implicitCallFlags = Lowerer::GetImplicitCallFlagsOpnd(bailOutFunc);
  1719. IR::IntConstOpnd * noImplicitCall = IR::IntConstOpnd::New(Js::ImplicitCall_None, TyInt8, bailOutFunc, true);
  1720. // Consider: if we are already doing implicit call in the outer loop, we don't need to clear the implicit call bit again
  1721. IR::Instr * clearImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, implicitCallFlags, noImplicitCall, bailOutFunc);
  1722. bailOutTarget->InsertBefore(clearImplicitCall);
  1723. IR::Instr * disableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, disableImplicitCallAndExceptionValue, bailOutFunc);
  1724. bailOutTarget->InsertBefore(disableImplicitCall);
  1725. endDisableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, enableImplicitCallAndExceptionValue, bailOutFunc);
  1726. bailOutTarget->InsertBefore(endDisableImplicitCall);
  1727. IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, IR::BailOutOnImplicitCalls, loop->bailOutInfo, loop->bailOutInfo->bailOutFunc);
  1728. bailOutInstr->SetSrc1(implicitCallFlags);
  1729. bailOutInstr->SetSrc2(noImplicitCall);
  1730. bailOutTarget->InsertBefore(bailOutInstr);
  1731. loop->endDisableImplicitCall = endDisableImplicitCall;
  1732. return endDisableImplicitCall;
  1733. }
  1734. #if DBG
  1735. bool
  1736. GlobOpt::IsHoistedPropertySym(PropertySym * sym) const
  1737. {
  1738. return IsHoistedPropertySym(sym->m_id, this->currentBlock->loop);
  1739. }
  1740. bool
  1741. GlobOpt::IsHoistedPropertySym(SymID symId, Loop * loop) const
  1742. {
  1743. StackSym * copySym;
  1744. return this->FindFieldHoistStackSym(loop, symId, &copySym) != nullptr;
  1745. }
  1746. bool
  1747. GlobOpt::IsPropertySymId(SymID symId) const
  1748. {
  1749. return this->func->m_symTable->Find(symId)->IsPropertySym();
  1750. }
  1751. void
  1752. GlobOpt::AssertCanCopyPropOrCSEFieldLoad(IR::Instr * instr)
  1753. {
  1754. // Consider: Hoisting LdRootFld may have complication with exception if the field doesn't exist.
  1755. // We need to have another opcode for the hoisted version to avoid the exception and bailout.
  1756. // Consider: Theoretically, we can copy prop/field hoist ScopedLdFld/ScopedStFld
  1757. // but GlobOtp::TransferSrcValue blocks that now, and copy prop into that instruction is not supported yet.
  1758. Assert(instr->m_opcode == Js::OpCode::LdSlot || instr->m_opcode == Js::OpCode::LdSlotArr
  1759. || instr->m_opcode == Js::OpCode::LdFld || instr->m_opcode == Js::OpCode::LdFldForCallApplyTarget
  1760. || instr->m_opcode == Js::OpCode::LdRootFld || instr->m_opcode == Js::OpCode::LdSuperFld
  1761. || instr->m_opcode == Js::OpCode::LdFldForTypeOf || instr->m_opcode == Js::OpCode::LdRootFldForTypeOf
  1762. || instr->m_opcode == Js::OpCode::LdMethodFld || instr->m_opcode == Js::OpCode::LdMethodFldPolyInlineMiss
  1763. || instr->m_opcode == Js::OpCode::LdRootMethodFld
  1764. || instr->m_opcode == Js::OpCode::LdMethodFromFlags
  1765. || instr->m_opcode == Js::OpCode::ScopedLdMethodFld
  1766. || instr->m_opcode == Js::OpCode::CheckFixedFld
  1767. || instr->m_opcode == Js::OpCode::CheckPropertyGuardAndLoadType);
  1768. Assert(instr->m_opcode == Js::OpCode::CheckFixedFld || instr->GetDst()->GetType() == TyVar);
  1769. Assert(instr->GetSrc1()->GetType() == TyVar);
  1770. Assert(instr->GetSrc1()->AsSymOpnd()->m_sym->IsPropertySym());
  1771. Assert(instr->GetSrc2() == nullptr);
  1772. }
  1773. #endif
  1774. StackSym *
  1775. GlobOpt::EnsureObjectTypeSym(StackSym * objectSym)
  1776. {
  1777. Assert(!objectSym->IsTypeSpec());
  1778. objectSym->EnsureObjectInfo(this->func);
  1779. if (objectSym->HasObjectTypeSym())
  1780. {
  1781. Assert(this->objectTypeSyms);
  1782. return objectSym->GetObjectTypeSym();
  1783. }
  1784. if (this->objectTypeSyms == nullptr)
  1785. {
  1786. this->objectTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  1787. }
  1788. StackSym * typeSym = StackSym::New(TyVar, this->func);
  1789. objectSym->GetObjectInfo()->m_typeSym = typeSym;
  1790. this->objectTypeSyms->Set(typeSym->m_id);
  1791. return typeSym;
  1792. }
  1793. PropertySym *
  1794. GlobOpt::EnsurePropertyWriteGuardSym(PropertySym * propertySym)
  1795. {
  1796. // Make sure that the PropertySym has a proto cache sym which is chained into the propertySym list.
  1797. if (!propertySym->m_writeGuardSym)
  1798. {
  1799. propertySym->m_writeGuardSym = PropertySym::New(propertySym->m_stackSym, propertySym->m_propertyId, (uint32)-1, (uint)-1, PropertyKindWriteGuard, this->func);
  1800. }
  1801. return propertySym->m_writeGuardSym;
  1802. }
  1803. void
  1804. GlobOpt::PreparePropertySymForTypeCheckSeq(PropertySym *propertySym)
  1805. {
  1806. Assert(!propertySym->m_stackSym->IsTypeSpec());
  1807. EnsureObjectTypeSym(propertySym->m_stackSym);
  1808. EnsurePropertyWriteGuardSym(propertySym);
  1809. }
  1810. bool
  1811. GlobOpt::IsPropertySymPreparedForTypeCheckSeq(PropertySym *propertySym)
  1812. {
  1813. Assert(!propertySym->m_stackSym->IsTypeSpec());
  1814. // The following doesn't need to be true. We may copy prop a constant into an object sym, which has
  1815. // previously been prepared for type check sequence optimization.
  1816. // Assert(!propertySym->m_stackSym->m_isIntConst || !propertySym->HasObjectTypeSym());
  1817. // The following doesn't need to be true. We may copy prop the object sym into a field load or store
  1818. // that doesn't have object type spec info and hence the operand wasn't prepared and doesn't have a write
  1819. // guard. The object sym, however, may have other field operations which are object type specialized and
  1820. // thus the type sym for it has been created.
  1821. // Assert(propertySym->HasObjectTypeSym() == propertySym->HasWriteGuardSym());
  1822. return propertySym->HasObjectTypeSym();
  1823. }
  1824. bool
  1825. GlobOpt::PreparePropertySymOpndForTypeCheckSeq(IR::PropertySymOpnd * propertySymOpnd, IR::Instr* instr, Loop * loop)
  1826. {
  1827. if (!DoFieldRefOpts(loop) || !OpCodeAttr::FastFldInstr(instr->m_opcode) || instr->CallsAccessor())
  1828. {
  1829. return false;
  1830. }
  1831. if (!propertySymOpnd->HasObjTypeSpecFldInfo())
  1832. {
  1833. return false;
  1834. }
  1835. JITObjTypeSpecFldInfo* info = propertySymOpnd->GetObjTypeSpecInfo();
  1836. if (info->UsesAccessor() || info->IsRootObjectNonConfigurableFieldLoad())
  1837. {
  1838. return false;
  1839. }
  1840. if (info->IsPoly() && !info->GetEquivalentTypeSet())
  1841. {
  1842. return false;
  1843. }
  1844. PropertySym * propertySym = propertySymOpnd->m_sym->AsPropertySym();
  1845. PreparePropertySymForTypeCheckSeq(propertySym);
  1846. propertySymOpnd->SetTypeCheckSeqCandidate(true);
  1847. propertySymOpnd->SetIsBeingStored(propertySymOpnd == instr->GetDst());
  1848. return true;
  1849. }
  1850. bool
  1851. GlobOpt::CheckIfPropOpEmitsTypeCheck(IR::Instr *instr, IR::PropertySymOpnd *opnd)
  1852. {
  1853. if (!DoFieldRefOpts() || !OpCodeAttr::FastFldInstr(instr->m_opcode))
  1854. {
  1855. return false;
  1856. }
  1857. if (!opnd->IsTypeCheckSeqCandidate())
  1858. {
  1859. return false;
  1860. }
  1861. return CheckIfInstrInTypeCheckSeqEmitsTypeCheck(instr, opnd);
  1862. }
  1863. IR::PropertySymOpnd *
  1864. GlobOpt::CreateOpndForTypeCheckOnly(IR::PropertySymOpnd* opnd, Func* func)
  1865. {
  1866. // Used only for CheckObjType instruction today. Future users should make a call
  1867. // whether the new operand is jit optimized in their scenario or not.
  1868. Assert(!opnd->IsRootObjectNonConfigurableFieldLoad());
  1869. IR::PropertySymOpnd *newOpnd = opnd->CopyCommon(func);
  1870. newOpnd->SetObjTypeSpecFldInfo(opnd->GetObjTypeSpecInfo());
  1871. newOpnd->SetUsesAuxSlot(opnd->UsesAuxSlot());
  1872. newOpnd->SetSlotIndex(opnd->GetSlotIndex());
  1873. newOpnd->objTypeSpecFlags = opnd->objTypeSpecFlags;
  1874. // If we're turning the instruction owning this operand into a CheckObjType, we will do a type check here
  1875. // only for the sake of downstream instructions, so the flags pertaining to this property access are
  1876. // irrelevant, because we don't do a property access here.
  1877. newOpnd->SetTypeCheckOnly(true);
  1878. newOpnd->usesFixedValue = false;
  1879. newOpnd->finalType = opnd->finalType;
  1880. newOpnd->guardedPropOps = opnd->guardedPropOps != nullptr ? opnd->guardedPropOps->CopyNew() : nullptr;
  1881. newOpnd->writeGuards = opnd->writeGuards != nullptr ? opnd->writeGuards->CopyNew() : nullptr;
  1882. newOpnd->SetIsJITOptimizedReg(true);
  1883. return newOpnd;
  1884. }
  1885. bool
  1886. GlobOpt::FinishOptPropOp(IR::Instr *instr, IR::PropertySymOpnd *opnd, BasicBlock* block, bool updateExistingValue, bool* emitsTypeCheckOut, bool* changesTypeValueOut)
  1887. {
  1888. if (!DoFieldRefOpts() || !OpCodeAttr::FastFldInstr(instr->m_opcode))
  1889. {
  1890. return false;
  1891. }
  1892. bool isTypeCheckSeqCandidate = opnd->IsTypeCheckSeqCandidate();
  1893. bool isObjTypeSpecialized = false;
  1894. bool isObjTypeChecked = false;
  1895. if (isTypeCheckSeqCandidate)
  1896. {
  1897. isObjTypeSpecialized = ProcessPropOpInTypeCheckSeq<true>(instr, opnd, block, updateExistingValue, emitsTypeCheckOut, changesTypeValueOut, &isObjTypeChecked);
  1898. }
  1899. if (opnd == instr->GetDst() && this->objectTypeSyms)
  1900. {
  1901. if (block == nullptr)
  1902. {
  1903. block = this->currentBlock;
  1904. }
  1905. // This is a property store that may change the layout of the object that it stores to. This means that
  1906. // it may change any aliased object. Do two things to address this:
  1907. // - Add all object types in this function to the set that may have had a property added. This will prevent
  1908. // final type optimization across this instruction. (Only needed here for non-specialized stores.)
  1909. // - Kill all type symbols that currently hold object-header-inlined types. Any of them may have their layout
  1910. // changed by the addition of a property.
  1911. SymID opndId = opnd->HasObjectTypeSym() ? opnd->GetObjectTypeSym()->m_id : -1;
  1912. if (!isObjTypeChecked)
  1913. {
  1914. if (block->globOptData.maybeWrittenTypeSyms == nullptr)
  1915. {
  1916. block->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  1917. }
  1918. if (isObjTypeSpecialized)
  1919. {
  1920. // The current object will be protected by a type check, unless no further accesses to it are
  1921. // protected by this access.
  1922. Assert(this->objectTypeSyms->Test(opndId));
  1923. this->objectTypeSyms->Clear(opndId);
  1924. }
  1925. block->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
  1926. if (isObjTypeSpecialized)
  1927. {
  1928. this->objectTypeSyms->Set(opndId);
  1929. }
  1930. }
  1931. if (!isObjTypeSpecialized || opnd->ChangesObjectLayout())
  1932. {
  1933. this->KillObjectHeaderInlinedTypeSyms(block, isObjTypeSpecialized, opndId);
  1934. }
  1935. }
  1936. return isObjTypeSpecialized;
  1937. }
  1938. void
  1939. GlobOpt::KillObjectHeaderInlinedTypeSyms(BasicBlock *block, bool isObjTypeSpecialized, SymID opndId)
  1940. {
  1941. if (this->objectTypeSyms == nullptr)
  1942. {
  1943. return;
  1944. }
  1945. FOREACH_BITSET_IN_SPARSEBV(symId, this->objectTypeSyms)
  1946. {
  1947. if (symId == opndId && isObjTypeSpecialized)
  1948. {
  1949. // The current object will be protected by a type check, unless no further accesses to it are
  1950. // protected by this access.
  1951. continue;
  1952. }
  1953. Value *value = this->FindObjectTypeValue(symId, block);
  1954. if (value)
  1955. {
  1956. JsTypeValueInfo *valueInfo = value->GetValueInfo()->AsJsType();
  1957. Assert(valueInfo);
  1958. if (valueInfo->GetJsType() != nullptr)
  1959. {
  1960. JITTypeHolder type(valueInfo->GetJsType());
  1961. if (Js::DynamicType::Is(type->GetTypeId()))
  1962. {
  1963. if (type->GetTypeHandler()->IsObjectHeaderInlinedTypeHandler())
  1964. {
  1965. this->blockData.liveFields->Clear(symId);
  1966. }
  1967. }
  1968. }
  1969. else if (valueInfo->GetJsTypeSet())
  1970. {
  1971. Js::EquivalentTypeSet *typeSet = valueInfo->GetJsTypeSet();
  1972. for (uint16 i = 0; i < typeSet->GetCount(); i++)
  1973. {
  1974. JITTypeHolder type = typeSet->GetType(i);
  1975. if (type != nullptr && Js::DynamicType::Is(type->GetTypeId()))
  1976. {
  1977. if (type->GetTypeHandler()->IsObjectHeaderInlinedTypeHandler())
  1978. {
  1979. this->blockData.liveFields->Clear(symId);
  1980. break;
  1981. }
  1982. }
  1983. }
  1984. }
  1985. }
  1986. }
  1987. NEXT_BITSET_IN_SPARSEBV;
  1988. }
  1989. bool
  1990. GlobOpt::AreTypeSetsIdentical(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet * rightTypeSet)
  1991. {
  1992. return Js::EquivalentTypeSet::AreIdentical(leftTypeSet, rightTypeSet);
  1993. }
  1994. bool
  1995. GlobOpt::IsSubsetOf(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet * rightTypeSet)
  1996. {
  1997. return Js::EquivalentTypeSet::IsSubsetOf(leftTypeSet, rightTypeSet);
  1998. }
  1999. bool
  2000. GlobOpt::CompareCurrentTypesWithExpectedTypes(JsTypeValueInfo *valueInfo, IR::PropertySymOpnd * propertySymOpnd)
  2001. {
  2002. bool isTypeDead = propertySymOpnd->IsTypeDead();
  2003. if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
  2004. {
  2005. // No upstream types. Do a type check.
  2006. return !isTypeDead;
  2007. }
  2008. if (!propertySymOpnd->HasEquivalentTypeSet() || propertySymOpnd->NeedsMonoCheck())
  2009. {
  2010. JITTypeHolder opndType = propertySymOpnd->GetType();
  2011. if (valueInfo->GetJsType() != nullptr)
  2012. {
  2013. if (valueInfo->GetJsType() == propertySymOpnd->GetType())
  2014. {
  2015. return true;
  2016. }
  2017. if (propertySymOpnd->HasInitialType() && valueInfo->GetJsType() == propertySymOpnd->GetInitialType())
  2018. {
  2019. return !isTypeDead;
  2020. }
  2021. return false;
  2022. }
  2023. else
  2024. {
  2025. Assert(valueInfo->GetJsTypeSet());
  2026. Js::EquivalentTypeSet *valueTypeSet = valueInfo->GetJsTypeSet();
  2027. if (valueTypeSet->Contains(opndType))
  2028. {
  2029. return !isTypeDead;
  2030. }
  2031. if (propertySymOpnd->HasInitialType() && valueTypeSet->Contains(propertySymOpnd->GetInitialType()))
  2032. {
  2033. return !isTypeDead;
  2034. }
  2035. return false;
  2036. }
  2037. }
  2038. else
  2039. {
  2040. Js::EquivalentTypeSet * opndTypeSet = propertySymOpnd->GetEquivalentTypeSet();
  2041. if (valueInfo->GetJsType() != nullptr)
  2042. {
  2043. uint16 checkedTypeSetIndex;
  2044. if (opndTypeSet->Contains(valueInfo->GetJsType(), &checkedTypeSetIndex))
  2045. {
  2046. return true;
  2047. }
  2048. return false;
  2049. }
  2050. else
  2051. {
  2052. if (IsSubsetOf(valueInfo->GetJsTypeSet(), opndTypeSet))
  2053. {
  2054. return true;
  2055. }
  2056. if (propertySymOpnd->IsMono() ?
  2057. valueInfo->GetJsTypeSet()->Contains(propertySymOpnd->GetFirstEquivalentType()) :
  2058. IsSubsetOf(opndTypeSet, valueInfo->GetJsTypeSet()))
  2059. {
  2060. return true;
  2061. }
  2062. return false;
  2063. }
  2064. }
  2065. }
  2066. bool
  2067. GlobOpt::ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd)
  2068. {
  2069. return ProcessPropOpInTypeCheckSeq<true>(instr, opnd, this->currentBlock, false);
  2070. }
  2071. bool GlobOpt::CheckIfInstrInTypeCheckSeqEmitsTypeCheck(IR::Instr* instr, IR::PropertySymOpnd *opnd)
  2072. {
  2073. bool emitsTypeCheck;
  2074. ProcessPropOpInTypeCheckSeq<false>(instr, opnd, this->currentBlock, false, &emitsTypeCheck);
  2075. return emitsTypeCheck;
  2076. }
  2077. template<bool makeChanges>
  2078. bool
  2079. GlobOpt::ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd, BasicBlock* block, bool updateExistingValue, bool* emitsTypeCheckOut, bool* changesTypeValueOut, bool *isTypeCheckedOut)
  2080. {
  2081. // We no longer mark types as dead in the backward pass, so we should never see an instr with a dead type here
  2082. // during the forward pass. For the time being we've retained the logic below to deal with dead types in case
  2083. // we ever wanted to revert back to more aggressive type killing that we had before.
  2084. Assert(!opnd->IsTypeDead());
  2085. Assert(opnd->IsTypeCheckSeqCandidate());
  2086. Assert(opnd->HasObjectTypeSym());
  2087. bool isStore = opnd == instr->GetDst();
  2088. bool isTypeDead = opnd->IsTypeDead();
  2089. bool consumeType = makeChanges && !IsLoopPrePass();
  2090. bool produceType = makeChanges && !isTypeDead;
  2091. bool isSpecialized = false;
  2092. bool emitsTypeCheck = false;
  2093. bool addsProperty = false;
  2094. if (block == nullptr)
  2095. {
  2096. block = this->currentBlock;
  2097. }
  2098. StackSym * typeSym = opnd->GetObjectTypeSym();
  2099. #if DBG
  2100. uint16 typeCheckSeqFlagsBefore;
  2101. Value* valueBefore = nullptr;
  2102. JsTypeValueInfo* valueInfoBefore = nullptr;
  2103. if (!makeChanges)
  2104. {
  2105. typeCheckSeqFlagsBefore = opnd->GetTypeCheckSeqFlags();
  2106. valueBefore = FindObjectTypeValue(typeSym, block);
  2107. if (valueBefore != nullptr)
  2108. {
  2109. Assert(valueBefore->GetValueInfo() != nullptr && valueBefore->GetValueInfo()->IsJsType());
  2110. valueInfoBefore = valueBefore->GetValueInfo()->AsJsType();
  2111. }
  2112. }
  2113. #endif
  2114. Value *value = this->FindObjectTypeValue(typeSym, block);
  2115. JsTypeValueInfo* valueInfo = value != nullptr ? value->GetValueInfo()->AsJsType() : nullptr;
  2116. if (consumeType && valueInfo != nullptr)
  2117. {
  2118. opnd->SetTypeAvailable(true);
  2119. }
  2120. bool doEquivTypeCheck = opnd->HasEquivalentTypeSet() && !opnd->NeedsMonoCheck();
  2121. if (!doEquivTypeCheck)
  2122. {
  2123. // We need a monomorphic type check here (e.g., final type opt, fixed field check on non-proto property).
  2124. JITTypeHolder opndType = opnd->GetType();
  2125. if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
  2126. {
  2127. // This is the initial type check.
  2128. opnd->SetTypeAvailable(false);
  2129. isSpecialized = !isTypeDead;
  2130. emitsTypeCheck = isSpecialized;
  2131. addsProperty = isStore && isSpecialized && opnd->HasInitialType();
  2132. if (produceType)
  2133. {
  2134. SetObjectTypeFromTypeSym(typeSym, opndType, nullptr, block, updateExistingValue);
  2135. }
  2136. }
  2137. else if (valueInfo->GetJsType() != nullptr)
  2138. {
  2139. // We have a monomorphic type check upstream. Check against initial/final type.
  2140. const JITTypeHolder valueType(valueInfo->GetJsType());
  2141. if (valueType == opndType)
  2142. {
  2143. // The type on this instruction matches the live value in the value table, so there is no need to
  2144. // refresh the value table.
  2145. isSpecialized = true;
  2146. if (isTypeCheckedOut)
  2147. {
  2148. *isTypeCheckedOut = true;
  2149. }
  2150. if (consumeType)
  2151. {
  2152. opnd->SetTypeChecked(true);
  2153. }
  2154. }
  2155. else if (opnd->HasInitialType() && valueType == opnd->GetInitialType())
  2156. {
  2157. // Checked type matches the initial type at this store.
  2158. bool objectMayHaveAcquiredAdditionalProperties =
  2159. block->globOptData.maybeWrittenTypeSyms &&
  2160. block->globOptData.maybeWrittenTypeSyms->Test(typeSym->m_id);
  2161. if (consumeType)
  2162. {
  2163. opnd->SetTypeChecked(!objectMayHaveAcquiredAdditionalProperties);
  2164. opnd->SetInitialTypeChecked(!objectMayHaveAcquiredAdditionalProperties);
  2165. }
  2166. if (produceType)
  2167. {
  2168. SetObjectTypeFromTypeSym(typeSym, opndType, nullptr, block, updateExistingValue);
  2169. }
  2170. isSpecialized = !isTypeDead || !objectMayHaveAcquiredAdditionalProperties;
  2171. emitsTypeCheck = isSpecialized && objectMayHaveAcquiredAdditionalProperties;
  2172. addsProperty = isSpecialized;
  2173. if (isTypeCheckedOut)
  2174. {
  2175. *isTypeCheckedOut = !objectMayHaveAcquiredAdditionalProperties;
  2176. }
  2177. }
  2178. else
  2179. {
  2180. // This must be a type mismatch situation, because the value is available, but doesn't match either
  2181. // the current type or the initial type. We will not optimize this instruction and we do not produce
  2182. // a new type value here.
  2183. isSpecialized = false;
  2184. if (consumeType)
  2185. {
  2186. opnd->SetTypeMismatch(true);
  2187. }
  2188. }
  2189. }
  2190. else
  2191. {
  2192. // We have an equivalent type check upstream, but we require a particular type at this point. We
  2193. // can't treat it as "checked", but we may benefit from checking for the required type.
  2194. Assert(valueInfo->GetJsTypeSet());
  2195. Js::EquivalentTypeSet *valueTypeSet = valueInfo->GetJsTypeSet();
  2196. if (valueTypeSet->Contains(opndType))
  2197. {
  2198. // Required type is in the type set we've checked. Check for the required type here, and
  2199. // note in the value info that we've narrowed down to this type. (But leave the type set in the
  2200. // value info so it can be merged with the same type set on other paths.)
  2201. isSpecialized = !isTypeDead;
  2202. emitsTypeCheck = isSpecialized;
  2203. if (produceType)
  2204. {
  2205. SetSingleTypeOnObjectTypeValue(value, opndType);
  2206. }
  2207. }
  2208. else if (opnd->HasInitialType() && valueTypeSet->Contains(opnd->GetInitialType()))
  2209. {
  2210. // Required initial type is in the type set we've checked. Check for the initial type here, and
  2211. // note in the value info that we've narrowed down to this type. (But leave the type set in the
  2212. // value info so it can be merged with the same type set on other paths.)
  2213. isSpecialized = !isTypeDead;
  2214. emitsTypeCheck = isSpecialized;
  2215. addsProperty = isSpecialized;
  2216. if (produceType)
  2217. {
  2218. SetSingleTypeOnObjectTypeValue(value, opndType);
  2219. }
  2220. }
  2221. else
  2222. {
  2223. // This must be a type mismatch situation, because the value is available, but doesn't match either
  2224. // the current type or the initial type. We will not optimize this instruction and we do not produce
  2225. // a new type value here.
  2226. isSpecialized = false;
  2227. if (consumeType)
  2228. {
  2229. opnd->SetTypeMismatch(true);
  2230. }
  2231. }
  2232. }
  2233. }
  2234. else
  2235. {
  2236. Assert(!opnd->NeedsMonoCheck());
  2237. Js::EquivalentTypeSet * opndTypeSet = opnd->GetEquivalentTypeSet();
  2238. uint16 checkedTypeSetIndex = (uint16)-1;
  2239. if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
  2240. {
  2241. // If we don't have a value for the type we will have to emit a type check and we produce a new type value here.
  2242. if (produceType)
  2243. {
  2244. if (opnd->IsMono())
  2245. {
  2246. SetObjectTypeFromTypeSym(typeSym, opnd->GetFirstEquivalentType(), nullptr, block, updateExistingValue);
  2247. }
  2248. else
  2249. {
  2250. SetObjectTypeFromTypeSym(typeSym, nullptr, opndTypeSet, block, updateExistingValue);
  2251. }
  2252. }
  2253. isSpecialized = !isTypeDead;
  2254. emitsTypeCheck = isSpecialized;
  2255. }
  2256. else if (valueInfo->GetJsType() != nullptr ?
  2257. opndTypeSet->Contains(valueInfo->GetJsType(), &checkedTypeSetIndex) :
  2258. IsSubsetOf(valueInfo->GetJsTypeSet(), opndTypeSet))
  2259. {
  2260. // All the types in the value info are contained in the set required by this access,
  2261. // meaning that they're equivalent to the opnd's type set.
  2262. // We won't have a type check, and we don't need to touch the type value.
  2263. isSpecialized = true;
  2264. if (isTypeCheckedOut)
  2265. {
  2266. *isTypeCheckedOut = true;
  2267. }
  2268. if (consumeType)
  2269. {
  2270. opnd->SetTypeChecked(true);
  2271. }
  2272. if (checkedTypeSetIndex != (uint16)-1)
  2273. {
  2274. opnd->SetCheckedTypeSetIndex(checkedTypeSetIndex);
  2275. }
  2276. }
  2277. else if (valueInfo->GetJsTypeSet() &&
  2278. (opnd->IsMono() ?
  2279. valueInfo->GetJsTypeSet()->Contains(opnd->GetFirstEquivalentType()) :
  2280. IsSubsetOf(opndTypeSet, valueInfo->GetJsTypeSet())
  2281. )
  2282. )
  2283. {
  2284. // We have an equivalent type check upstream, but we require a tighter type check at this point.
  2285. // We can't treat the operand as "checked", but check for equivalence with the tighter set and update the
  2286. // value info.
  2287. if (produceType)
  2288. {
  2289. if (opnd->IsMono())
  2290. {
  2291. SetObjectTypeFromTypeSym(typeSym, opnd->GetFirstEquivalentType(), nullptr, block, updateExistingValue);
  2292. }
  2293. else
  2294. {
  2295. SetObjectTypeFromTypeSym(typeSym, nullptr, opndTypeSet, block, updateExistingValue);
  2296. }
  2297. }
  2298. isSpecialized = !isTypeDead;
  2299. emitsTypeCheck = isSpecialized;
  2300. }
  2301. else
  2302. {
  2303. // This must be a type mismatch situation, because the value is available, but doesn't match either
  2304. // the current type or the initial type. We will not optimize this instruction and we do not produce
  2305. // a new type value here.
  2306. isSpecialized = false;
  2307. if (consumeType)
  2308. {
  2309. opnd->SetTypeMismatch(true);
  2310. }
  2311. }
  2312. }
  2313. Assert(isSpecialized || (!emitsTypeCheck && !addsProperty));
  2314. if (consumeType && opnd->MayNeedWriteGuardProtection())
  2315. {
  2316. Assert(!isStore);
  2317. PropertySym *propertySym = opnd->m_sym->AsPropertySym();
  2318. Assert(propertySym->m_writeGuardSym);
  2319. opnd->SetWriteGuardChecked(!!block->globOptData.liveFields->Test(propertySym->m_writeGuardSym->m_id));
  2320. }
  2321. // Even specialized property adds must kill all types for other property adds. That's because any other object sym
  2322. // may, in fact, be an alias of the instance whose type is being modified here. (see Windows Blue Bug 541876)
  2323. if (makeChanges && addsProperty)
  2324. {
  2325. Assert(isStore && isSpecialized);
  2326. Assert(this->objectTypeSyms != nullptr);
  2327. Assert(this->objectTypeSyms->Test(typeSym->m_id));
  2328. if (block->globOptData.maybeWrittenTypeSyms == nullptr)
  2329. {
  2330. block->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  2331. }
  2332. this->objectTypeSyms->Clear(typeSym->m_id);
  2333. block->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
  2334. this->objectTypeSyms->Set(typeSym->m_id);
  2335. }
  2336. if (produceType && emitsTypeCheck && opnd->IsMono())
  2337. {
  2338. // Consider (ObjTypeSpec): Represent maybeWrittenTypeSyms as a flag on value info of the type sym.
  2339. if (block->globOptData.maybeWrittenTypeSyms != nullptr)
  2340. {
  2341. // We're doing a type check here, so objtypespec of property adds is safe for this type
  2342. // from this point forward.
  2343. block->globOptData.maybeWrittenTypeSyms->Clear(typeSym->m_id);
  2344. }
  2345. }
  2346. // Consider (ObjTypeSpec): Enable setting write guards live on instructions hoisted out of loops. Note that produceType
  2347. // is false if the type values on loop back edges don't match (see earlier comments).
  2348. // This means that hoisted instructions won't set write guards live if the type changes in the loop, even if
  2349. // the corresponding properties have not been written inside the loop. This may result in some unnecessary type
  2350. // checks and bailouts inside the loop. To enable this, we would need to verify the write guards are still live
  2351. // on the back edge (much like we're doing for types above).
  2352. // Consider (ObjTypeSpec): Support polymorphic write guards as well. We can't currently distinguish between mono and
  2353. // poly write guards, and a type check can only protect operations matching with respect to polymorphism (see
  2354. // BackwardPass::TrackObjTypeSpecProperties for details), so for now we only target monomorphic operations.
  2355. if (produceType && emitsTypeCheck && opnd->IsMono())
  2356. {
  2357. // If the type check we'll emit here protects some property operations that require a write guard (i.e.
  2358. // they must do an extra type check and property guard check, if they have been written to in this
  2359. // function), let's mark the write guards as live here, so we can accurately track if their properties
  2360. // have been written to. Make sure we only set those that we'll actually guard, i.e. those that match
  2361. // with respect to polymorphism.
  2362. if (opnd->GetWriteGuards() != nullptr)
  2363. {
  2364. block->globOptData.liveFields->Or(opnd->GetWriteGuards());
  2365. }
  2366. }
  2367. if (makeChanges && isTypeDead)
  2368. {
  2369. this->KillObjectType(opnd->GetObjectSym(), block->globOptData.liveFields);
  2370. }
  2371. #if DBG
  2372. if (!makeChanges)
  2373. {
  2374. uint16 typeCheckSeqFlagsAfter = opnd->GetTypeCheckSeqFlags();
  2375. Assert(typeCheckSeqFlagsBefore == typeCheckSeqFlagsAfter);
  2376. Value* valueAfter = FindObjectTypeValue(typeSym, block);
  2377. Assert(valueBefore == valueAfter);
  2378. if (valueAfter != nullptr)
  2379. {
  2380. Assert(valueBefore != nullptr);
  2381. Assert(valueAfter->GetValueInfo() != nullptr && valueAfter->GetValueInfo()->IsJsType());
  2382. JsTypeValueInfo* valueInfoAfter = valueAfter->GetValueInfo()->AsJsType();
  2383. Assert(valueInfoBefore == valueInfoAfter);
  2384. Assert(valueInfoBefore->GetJsType() == valueInfoAfter->GetJsType());
  2385. Assert(valueInfoBefore->GetJsTypeSet() == valueInfoAfter->GetJsTypeSet());
  2386. }
  2387. }
  2388. #endif
  2389. if (emitsTypeCheckOut != nullptr)
  2390. {
  2391. *emitsTypeCheckOut = emitsTypeCheck;
  2392. }
  2393. if (changesTypeValueOut != nullptr)
  2394. {
  2395. *changesTypeValueOut = isSpecialized && (emitsTypeCheck || addsProperty);
  2396. }
  2397. return isSpecialized;
  2398. }
  2399. IR::Instr*
  2400. GlobOpt::OptNewScObject(IR::Instr** instrPtr, Value* srcVal)
  2401. {
  2402. IR::Instr *&instr = *instrPtr;
  2403. if (IsLoopPrePass())
  2404. {
  2405. return instr;
  2406. }
  2407. if (PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->func) || !this->DoFieldRefOpts())
  2408. {
  2409. return instr;
  2410. }
  2411. if (!instr->IsNewScObjectInstr())
  2412. {
  2413. return nullptr;
  2414. }
  2415. bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor;
  2416. const JITTimeConstructorCache * ctorCache = instr->IsProfiledInstr() ?
  2417. instr->m_func->GetConstructorCache(static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId)) : nullptr;
  2418. // TODO: OOP JIT, enable assert
  2419. //Assert(ctorCache == nullptr || srcVal->GetValueInfo()->IsVarConstant() && Js::JavascriptFunction::Is(srcVal->GetValueInfo()->AsVarConstant()->VarValue()));
  2420. Assert(ctorCache == nullptr || !ctorCache->IsTypeFinal() || ctorCache->CtorHasNoExplicitReturnValue());
  2421. if (ctorCache != nullptr && !ctorCache->SkipNewScObject() && (isCtorInlined || ctorCache->IsTypeFinal()))
  2422. {
  2423. GenerateBailAtOperation(instrPtr, IR::BailOutFailedCtorGuardCheck);
  2424. }
  2425. return instr;
  2426. }
  2427. void
  2428. GlobOpt::ValueNumberObjectType(IR::Opnd *dstOpnd, IR::Instr *instr)
  2429. {
  2430. if (!dstOpnd->IsRegOpnd())
  2431. {
  2432. return;
  2433. }
  2434. if (dstOpnd->AsRegOpnd()->m_sym->IsTypeSpec())
  2435. {
  2436. return;
  2437. }
  2438. if (instr->IsNewScObjectInstr())
  2439. {
  2440. // If we have a NewScObj* for which we have a valid constructor cache we know what type the created object will have.
  2441. // Let's produce the type value accordingly so we don't insert a type check and bailout in the constructor and
  2442. // potentially further downstream.
  2443. Assert(!PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->func) || !instr->HasBailOutInfo());
  2444. if (instr->HasBailOutInfo())
  2445. {
  2446. Assert(instr->IsProfiledInstr());
  2447. Assert(instr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck);
  2448. bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor;
  2449. JITTimeConstructorCache * ctorCache = instr->m_func->GetConstructorCache(static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId));
  2450. Assert(ctorCache != nullptr && (isCtorInlined || ctorCache->IsTypeFinal()));
  2451. StackSym* objSym = dstOpnd->AsRegOpnd()->m_sym;
  2452. StackSym* dstTypeSym = EnsureObjectTypeSym(objSym);
  2453. Assert(this->FindValue(dstTypeSym) == nullptr);
  2454. SetObjectTypeFromTypeSym(dstTypeSym, ctorCache->GetType(), nullptr);
  2455. }
  2456. }
  2457. else
  2458. {
  2459. // If the dst opnd is a reg that has a type sym associated with it, then we are either killing
  2460. // the type's existing value or (in the case of a reg copy) assigning it the value of
  2461. // the src's type sym (if any). If the dst doesn't have a type sym, but the src does, let's
  2462. // give dst a new type sym and transfer the value.
  2463. Value *newValue = nullptr;
  2464. IR::Opnd * srcOpnd = instr->GetSrc1();
  2465. if (instr->m_opcode == Js::OpCode::Ld_A && srcOpnd->IsRegOpnd() &&
  2466. !srcOpnd->AsRegOpnd()->m_sym->IsTypeSpec() && srcOpnd->AsRegOpnd()->m_sym->HasObjectTypeSym())
  2467. {
  2468. StackSym *srcTypeSym = srcOpnd->AsRegOpnd()->m_sym->GetObjectTypeSym();
  2469. newValue = this->FindValue(srcTypeSym);
  2470. }
  2471. if (newValue == nullptr)
  2472. {
  2473. if (dstOpnd->AsRegOpnd()->m_sym->HasObjectTypeSym())
  2474. {
  2475. StackSym * typeSym = dstOpnd->AsRegOpnd()->m_sym->GetObjectTypeSym();
  2476. this->blockData.symToValueMap->Clear(typeSym->m_id);
  2477. }
  2478. }
  2479. else
  2480. {
  2481. Assert(newValue->GetValueInfo()->IsJsType());
  2482. StackSym * typeSym;
  2483. if (!dstOpnd->AsRegOpnd()->m_sym->HasObjectTypeSym())
  2484. {
  2485. typeSym = nullptr;
  2486. }
  2487. typeSym = EnsureObjectTypeSym(dstOpnd->AsRegOpnd()->m_sym);
  2488. this->SetValue(&this->blockData, newValue, typeSym);
  2489. }
  2490. }
  2491. }
  2492. IR::Instr *
  2493. GlobOpt::SetTypeCheckBailOut(IR::Opnd *opnd, IR::Instr *instr, BailOutInfo *bailOutInfo)
  2494. {
  2495. if (this->IsLoopPrePass() || !opnd->IsSymOpnd())
  2496. {
  2497. return instr;
  2498. }
  2499. if (!opnd->AsSymOpnd()->IsPropertySymOpnd())
  2500. {
  2501. return instr;
  2502. }
  2503. IR::PropertySymOpnd * propertySymOpnd = opnd->AsPropertySymOpnd();
  2504. AssertMsg(propertySymOpnd->TypeCheckSeqBitsSetOnlyIfCandidate(), "Property sym operand optimized despite not being a candidate?");
  2505. AssertMsg(bailOutInfo == nullptr || !instr->HasBailOutInfo(), "Why are we adding new bailout info to an instruction that already has it?");
  2506. auto HandleBailout = [&](IR::BailOutKind bailOutKind)->void {
  2507. // At this point, we have a cached type that is live downstream or the type check is required
  2508. // for a fixed field load. If we can't do away with the type check, then we're going to need bailout,
  2509. // so lets add bailout info if we don't already have it.
  2510. if (!instr->HasBailOutInfo())
  2511. {
  2512. if (bailOutInfo)
  2513. {
  2514. instr = instr->ConvertToBailOutInstr(bailOutInfo, bailOutKind);
  2515. }
  2516. else
  2517. {
  2518. GenerateBailAtOperation(&instr, bailOutKind);
  2519. BailOutInfo *bailOutInfo = instr->GetBailOutInfo();
  2520. // Consider (ObjTypeSpec): If we're checking a fixed field here the bailout could be due to polymorphism or
  2521. // due to a fixed field turning non-fixed. Consider distinguishing between the two.
  2522. bailOutInfo->polymorphicCacheIndex = propertySymOpnd->m_inlineCacheIndex;
  2523. }
  2524. }
  2525. else if (instr->GetBailOutKind() == IR::BailOutMarkTempObject)
  2526. {
  2527. Assert(!bailOutInfo);
  2528. Assert(instr->GetBailOutInfo()->polymorphicCacheIndex == -1);
  2529. instr->SetBailOutKind(bailOutKind | IR::BailOutMarkTempObject);
  2530. instr->GetBailOutInfo()->polymorphicCacheIndex = propertySymOpnd->m_inlineCacheIndex;
  2531. }
  2532. else
  2533. {
  2534. Assert(bailOutKind == instr->GetBailOutKind());
  2535. }
  2536. };
  2537. bool isTypeCheckProtected;
  2538. IR::BailOutKind bailOutKind;
  2539. if (GlobOpt::NeedsTypeCheckBailOut(instr, propertySymOpnd, opnd == instr->GetDst(), &isTypeCheckProtected, &bailOutKind))
  2540. {
  2541. HandleBailout(bailOutKind);
  2542. }
  2543. else
  2544. {
  2545. if (instr->m_opcode == Js::OpCode::LdMethodFromFlags)
  2546. {
  2547. // If LdMethodFromFlags is hoisted to the top of the loop, we should share the same bailout Info.
  2548. // We don't need to do anything for LdMethodFromFlags that cannot be field hoisted.
  2549. HandleBailout(IR::BailOutFailedInlineTypeCheck);
  2550. }
  2551. else if (instr->HasBailOutInfo())
  2552. {
  2553. // If we already have a bailout info, but don't actually need it, let's remove it. This can happen if
  2554. // a CheckFixedFld added by the inliner (with bailout info) determined that the object's type has
  2555. // been checked upstream and no bailout is necessary here.
  2556. if (instr->m_opcode == Js::OpCode::CheckFixedFld)
  2557. {
  2558. AssertMsg(!PHASE_OFF(Js::FixedMethodsPhase, instr->m_func) ||
  2559. !PHASE_OFF(Js::UseFixedDataPropsPhase, instr->m_func), "CheckFixedFld with fixed method/data phase disabled?");
  2560. Assert(isTypeCheckProtected);
  2561. AssertMsg(instr->GetBailOutKind() == IR::BailOutFailedFixedFieldTypeCheck || instr->GetBailOutKind() == IR::BailOutFailedEquivalentFixedFieldTypeCheck,
  2562. "Only BailOutFailed[Equivalent]FixedFieldTypeCheck can be safely removed. Why does CheckFixedFld carry a different bailout kind?.");
  2563. instr->ClearBailOutInfo();
  2564. }
  2565. else if (propertySymOpnd->MayNeedTypeCheckProtection() && propertySymOpnd->IsTypeCheckProtected())
  2566. {
  2567. // Both the type and (if necessary) the proto object have been checked.
  2568. // We're doing a direct slot access. No possibility of bailout here (not even implicit call).
  2569. Assert(instr->GetBailOutKind() == IR::BailOutMarkTempObject);
  2570. instr->ClearBailOutInfo();
  2571. }
  2572. }
  2573. }
  2574. return instr;
  2575. }
  2576. void
  2577. GlobOpt::SetSingleTypeOnObjectTypeValue(Value* value, const JITTypeHolder type)
  2578. {
  2579. UpdateObjectTypeValue(value, type, true, nullptr, false);
  2580. }
  2581. void
  2582. GlobOpt::SetTypeSetOnObjectTypeValue(Value* value, Js::EquivalentTypeSet* typeSet)
  2583. {
  2584. UpdateObjectTypeValue(value, nullptr, false, typeSet, true);
  2585. }
  2586. void
  2587. GlobOpt::UpdateObjectTypeValue(Value* value, const JITTypeHolder type, bool setType, Js::EquivalentTypeSet* typeSet, bool setTypeSet)
  2588. {
  2589. Assert(value->GetValueInfo() != nullptr && value->GetValueInfo()->IsJsType());
  2590. JsTypeValueInfo* valueInfo = value->GetValueInfo()->AsJsType();
  2591. if (valueInfo->GetIsShared())
  2592. {
  2593. valueInfo = valueInfo->Copy(this->alloc);
  2594. value->SetValueInfo(valueInfo);
  2595. }
  2596. if (setType)
  2597. {
  2598. valueInfo->SetJsType(type);
  2599. }
  2600. if (setTypeSet)
  2601. {
  2602. valueInfo->SetJsTypeSet(typeSet);
  2603. }
  2604. }
  2605. void
  2606. GlobOpt::SetObjectTypeFromTypeSym(StackSym *typeSym, Value* value, BasicBlock* block)
  2607. {
  2608. Assert(typeSym != nullptr);
  2609. Assert(value != nullptr);
  2610. Assert(value->GetValueInfo() != nullptr && value->GetValueInfo()->IsJsType());
  2611. SymID typeSymId = typeSym->m_id;
  2612. if (block == nullptr)
  2613. {
  2614. block = this->currentBlock;
  2615. }
  2616. SetValue(&block->globOptData, value, typeSym);
  2617. block->globOptData.liveFields->Set(typeSymId);
  2618. }
  2619. void
  2620. GlobOpt::SetObjectTypeFromTypeSym(StackSym *typeSym, const JITTypeHolder type, Js::EquivalentTypeSet * typeSet, BasicBlock* block, bool updateExistingValue)
  2621. {
  2622. if (block == nullptr)
  2623. {
  2624. block = this->currentBlock;
  2625. }
  2626. SetObjectTypeFromTypeSym(typeSym, type, typeSet, &block->globOptData, updateExistingValue);
  2627. }
  2628. void
  2629. GlobOpt::SetObjectTypeFromTypeSym(StackSym *typeSym, const JITTypeHolder type, Js::EquivalentTypeSet * typeSet, GlobOptBlockData *blockData, bool updateExistingValue)
  2630. {
  2631. Assert(typeSym != nullptr);
  2632. SymID typeSymId = typeSym->m_id;
  2633. if (blockData == nullptr)
  2634. {
  2635. blockData = &this->blockData;
  2636. }
  2637. if (updateExistingValue)
  2638. {
  2639. Value* value = FindValueFromHashTable(blockData->symToValueMap, typeSymId);
  2640. // If we're trying to update an existing value, the value better exist. We only do this when updating a generic
  2641. // value created during loop pre-pass for field hoisting, so we expect the value info to still be blank.
  2642. Assert(value != nullptr && value->GetValueInfo() != nullptr && value->GetValueInfo()->IsJsType());
  2643. JsTypeValueInfo* valueInfo = value->GetValueInfo()->AsJsType();
  2644. Assert(valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr);
  2645. UpdateObjectTypeValue(value, type, true, typeSet, true);
  2646. }
  2647. else
  2648. {
  2649. JsTypeValueInfo* valueInfo = JsTypeValueInfo::New(this->alloc, type, typeSet);
  2650. this->SetSymStoreDirect(valueInfo, typeSym);
  2651. Value* value = NewValue(valueInfo);
  2652. SetValue(blockData, value, typeSym);
  2653. }
  2654. blockData->liveFields->Set(typeSymId);
  2655. }
  2656. void
  2657. GlobOpt::KillObjectType(StackSym* objectSym, BVSparse<JitArenaAllocator>* liveFields)
  2658. {
  2659. if (objectSym->IsTypeSpec())
  2660. {
  2661. objectSym = objectSym->GetVarEquivSym(this->func);
  2662. }
  2663. Assert(objectSym);
  2664. // We may be conservatively attempting to kill type syms from object syms that don't actually
  2665. // participate in object type specialization and hence don't actually have type syms (yet).
  2666. if (!objectSym->HasObjectTypeSym())
  2667. {
  2668. return;
  2669. }
  2670. if (liveFields == nullptr)
  2671. {
  2672. liveFields = this->blockData.liveFields;
  2673. }
  2674. liveFields->Clear(objectSym->GetObjectTypeSym()->m_id);
  2675. }
  2676. void
  2677. GlobOpt::KillAllObjectTypes(BVSparse<JitArenaAllocator>* liveFields)
  2678. {
  2679. if (this->objectTypeSyms)
  2680. {
  2681. if (liveFields == nullptr)
  2682. {
  2683. liveFields = this->blockData.liveFields;
  2684. }
  2685. liveFields->Minus(this->objectTypeSyms);
  2686. }
  2687. }
  2688. void
  2689. GlobOpt::EndFieldLifetime(IR::SymOpnd *symOpnd)
  2690. {
  2691. this->blockData.liveFields->Clear(symOpnd->m_sym->m_id);
  2692. }
  2693. PropertySym *
  2694. GlobOpt::CopyPropPropertySymObj(IR::SymOpnd *symOpnd, IR::Instr *instr)
  2695. {
  2696. Assert(symOpnd->m_sym->IsPropertySym());
  2697. PropertySym *propertySym = symOpnd->m_sym->AsPropertySym();
  2698. StackSym *objSym = propertySym->m_stackSym;
  2699. Value * val = this->FindValue(objSym);
  2700. if (val && !PHASE_OFF(Js::ObjPtrCopyPropPhase, this->func))
  2701. {
  2702. StackSym *copySym = this->GetCopyPropSym(objSym, val);
  2703. if (copySym != nullptr)
  2704. {
  2705. PropertySym *newProp = PropertySym::FindOrCreate(
  2706. copySym->m_id, propertySym->m_propertyId, propertySym->GetPropertyIdIndex(), propertySym->GetInlineCacheIndex(), propertySym->m_fieldKind, this->func);
  2707. if (!this->IsLoopPrePass() || (objSym->IsSingleDef() && copySym->IsSingleDef()))
  2708. {
  2709. #if DBG_DUMP
  2710. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::GlobOptPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  2711. {
  2712. Output::Print(_u("TRACE: "));
  2713. symOpnd->Dump();
  2714. Output::Print(_u(" : "));
  2715. Output::Print(_u("Copy prop obj ptr s%d, new property: "), copySym->m_id);
  2716. newProp->Dump();
  2717. Output::Print(_u("\n"));
  2718. }
  2719. #endif
  2720. // Copy prop
  2721. this->CaptureByteCodeSymUses(instr);
  2722. // If the old sym was part of an object type spec type check sequence,
  2723. // let's make sure the new one is prepped for it as well.
  2724. if (IsPropertySymPreparedForTypeCheckSeq(propertySym))
  2725. {
  2726. PreparePropertySymForTypeCheckSeq(newProp);
  2727. }
  2728. symOpnd->m_sym = newProp;
  2729. symOpnd->SetIsJITOptimizedReg(true);
  2730. if (symOpnd->IsPropertySymOpnd())
  2731. {
  2732. IR::PropertySymOpnd *propertySymOpnd = symOpnd->AsPropertySymOpnd();
  2733. if (propertySymOpnd->IsTypeCheckSeqCandidate())
  2734. {
  2735. // If the new pointer sym's expected type(s) don't match those in the inline-cache-based data for this access,
  2736. // we probably have a mismatch and can't safely objtypespec. If the saved objtypespecfldinfo isn't right for
  2737. // the new type, then we'll do an incorrect property access.
  2738. StackSym * newTypeSym = copySym->GetObjectTypeSym();
  2739. Value * newValue = this->FindObjectTypeValueNoLivenessCheck(newTypeSym, this->currentBlock);
  2740. JsTypeValueInfo * newValueInfo = newValue ? newValue->GetValueInfo()->AsJsType() : nullptr;
  2741. bool shouldOptimize = CompareCurrentTypesWithExpectedTypes(newValueInfo, propertySymOpnd);
  2742. if (!shouldOptimize)
  2743. {
  2744. propertySymOpnd->SetTypeCheckSeqCandidate(false);
  2745. }
  2746. }
  2747. // This is no longer strictly necessary, since we don't set the type dead bits in the initial
  2748. // backward pass, but let's keep it around for now in case we choose to revert to the old model.
  2749. propertySymOpnd->SetTypeDeadIfTypeCheckSeqCandidate(false);
  2750. }
  2751. if (this->IsLoopPrePass())
  2752. {
  2753. this->prePassCopyPropSym->Set(copySym->m_id);
  2754. }
  2755. }
  2756. propertySym = newProp;
  2757. if(instr->GetDst() && symOpnd->IsEqual(instr->GetDst()))
  2758. {
  2759. // Make sure any stack sym uses in the new destination property sym are unspecialized
  2760. instr = ToVarUses(instr, symOpnd, true, nullptr);
  2761. }
  2762. }
  2763. }
  2764. return propertySym;
  2765. }
  2766. void
  2767. GlobOpt::UpdateObjPtrValueType(IR::Opnd * opnd, IR::Instr * instr)
  2768. {
  2769. if (!opnd->IsSymOpnd() || !opnd->AsSymOpnd()->IsPropertySymOpnd())
  2770. {
  2771. return;
  2772. }
  2773. if (!instr->HasTypeCheckBailOut())
  2774. {
  2775. // No type check bailout, we didn't check that type of the object pointer.
  2776. return;
  2777. }
  2778. // Only check that fixed field should have type check bailout in loop prepass.
  2779. Assert(instr->m_opcode == Js::OpCode::CheckFixedFld || !this->IsLoopPrePass());
  2780. if (instr->m_opcode != Js::OpCode::CheckFixedFld)
  2781. {
  2782. // DeadStore pass may remove type check bailout, except CheckFixedFld which always needs
  2783. // type check bailout. So we can only change the type for CheckFixedFld.
  2784. // Consider: See if we can expand that in the future.
  2785. return;
  2786. }
  2787. IR::PropertySymOpnd * propertySymOpnd = opnd->AsPropertySymOpnd();
  2788. StackSym * objectSym = propertySymOpnd->GetObjectSym();
  2789. Value * objVal = this->FindValue(objectSym);
  2790. if (!objVal)
  2791. {
  2792. return;
  2793. }
  2794. ValueType objValueType = objVal->GetValueInfo()->Type();
  2795. if (objValueType.IsDefinite())
  2796. {
  2797. return;
  2798. }
  2799. // Verify that the types we're checking for here have been locked so that the type ID's can't be changed
  2800. // without changing the type.
  2801. if (!propertySymOpnd->HasObjectTypeSym())
  2802. {
  2803. return;
  2804. }
  2805. StackSym * typeSym = propertySymOpnd->GetObjectTypeSym();
  2806. Assert(typeSym);
  2807. Value * typeValue = this->FindObjectTypeValue(typeSym, currentBlock);
  2808. if (!typeValue)
  2809. {
  2810. return;
  2811. }
  2812. JsTypeValueInfo * typeValueInfo = typeValue->GetValueInfo()->AsJsType();
  2813. JITTypeHolder type = typeValueInfo->GetJsType();
  2814. if (type != nullptr)
  2815. {
  2816. if (Js::DynamicType::Is(type->GetTypeId()) &&
  2817. !type->GetTypeHandler()->IsLocked())
  2818. {
  2819. return;
  2820. }
  2821. }
  2822. else
  2823. {
  2824. Js::EquivalentTypeSet * typeSet = typeValueInfo->GetJsTypeSet();
  2825. Assert(typeSet);
  2826. for (uint16 i = 0; i < typeSet->GetCount(); i++)
  2827. {
  2828. type = typeSet->GetType(i);
  2829. if (Js::DynamicType::Is(type->GetTypeId()) &&
  2830. !type->GetTypeHandler()->IsLocked())
  2831. {
  2832. return;
  2833. }
  2834. }
  2835. }
  2836. AnalysisAssert(type != nullptr);
  2837. Js::TypeId typeId = type->GetTypeId();
  2838. // Passing false for useVirtual as we would never have a virtual typed array hitting this code path
  2839. ValueType newValueType = ValueType::FromTypeId(typeId, false);
  2840. if (newValueType == ValueType::Uninitialized)
  2841. {
  2842. switch (typeId)
  2843. {
  2844. default:
  2845. if (typeId > Js::TypeIds_LastStaticType)
  2846. {
  2847. Assert(typeId != Js::TypeIds_Proxy);
  2848. if (objValueType.IsLikelyArrayOrObjectWithArray())
  2849. {
  2850. // If we have likely object with array before, we can't make it definite object with array
  2851. // since we have only proved that it is an object.
  2852. // Keep the likely array or object with array.
  2853. }
  2854. else
  2855. {
  2856. newValueType = ValueType::GetObject(ObjectType::Object);
  2857. }
  2858. }
  2859. break;
  2860. case Js::TypeIds_Array:
  2861. // Because array can change type id, we can only make it definite if we are doing array check hoist
  2862. // so that implicit call will be installed between the array checks.
  2863. if (!DoArrayCheckHoist() ||
  2864. (currentBlock->loop
  2865. ? !this->ImplicitCallFlagsAllowOpts(currentBlock->loop)
  2866. : !this->ImplicitCallFlagsAllowOpts(this->func)))
  2867. {
  2868. break;
  2869. }
  2870. if (objValueType.IsLikelyArrayOrObjectWithArray())
  2871. {
  2872. // If we have likely no missing values before, keep the likely, because, we haven't proven that
  2873. // the array really has no missing values
  2874. if (!objValueType.HasNoMissingValues())
  2875. {
  2876. newValueType = ValueType::GetObject(ObjectType::Array).SetArrayTypeId(typeId);
  2877. }
  2878. }
  2879. else
  2880. {
  2881. newValueType = ValueType::GetObject(ObjectType::Array).SetArrayTypeId(typeId);
  2882. }
  2883. break;
  2884. }
  2885. }
  2886. if (newValueType != ValueType::Uninitialized)
  2887. {
  2888. ChangeValueType(currentBlock, objVal, newValueType, false, true);
  2889. }
  2890. }