GlobOptFields.cpp 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056
  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. bool
  7. GlobOpt::DoFieldCopyProp() const
  8. {
  9. BasicBlock *block = this->currentBlock;
  10. Loop *loop = block->loop;
  11. if (this->isRecursiveCallOnLandingPad)
  12. {
  13. // The landing pad at this point only contains load hosted by PRE.
  14. // These need to be copy-prop'd into the loop.
  15. // We want to look at the implicit-call info of the loop, not it's parent.
  16. Assert(block->IsLandingPad());
  17. loop = block->next->loop;
  18. Assert(loop);
  19. }
  20. return DoFieldCopyProp(loop);
  21. }
  22. bool
  23. GlobOpt::DoFunctionFieldCopyProp() const
  24. {
  25. return DoFieldCopyProp(nullptr);
  26. }
  27. bool
  28. GlobOpt::DoFieldCopyProp(Loop * loop) const
  29. {
  30. if (PHASE_OFF(Js::CopyPropPhase, this->func))
  31. {
  32. // Can't do field copy prop without copy prop
  33. return false;
  34. }
  35. if (PHASE_FORCE(Js::FieldCopyPropPhase, this->func))
  36. {
  37. // Force always turns on field copy prop
  38. return true;
  39. }
  40. if (PHASE_OFF(Js::FieldCopyPropPhase, this->func))
  41. {
  42. return false;
  43. }
  44. return this->DoFieldOpts(loop);
  45. }
  46. bool
  47. GlobOpt::DoObjTypeSpec() const
  48. {
  49. return this->DoObjTypeSpec(this->currentBlock->loop);
  50. }
  51. bool
  52. GlobOpt::DoObjTypeSpec(Loop *loop) const
  53. {
  54. if (!this->func->DoFastPaths())
  55. {
  56. return false;
  57. }
  58. if (PHASE_FORCE(Js::ObjTypeSpecPhase, this->func))
  59. {
  60. return true;
  61. }
  62. if (PHASE_OFF(Js::ObjTypeSpecPhase, this->func))
  63. {
  64. return false;
  65. }
  66. if (this->func->IsLoopBody() && this->func->HasProfileInfo() && this->func->GetReadOnlyProfileInfo()->IsObjTypeSpecDisabledInJitLoopBody())
  67. {
  68. return false;
  69. }
  70. if (this->ImplicitCallFlagsAllowOpts(this->func))
  71. {
  72. Assert(loop == nullptr || loop->CanDoFieldCopyProp());
  73. return true;
  74. }
  75. return loop != nullptr && loop->CanDoFieldCopyProp();
  76. }
  77. bool
  78. GlobOpt::DoFieldOpts(Loop * loop) const
  79. {
  80. if (this->ImplicitCallFlagsAllowOpts(this->func))
  81. {
  82. Assert(loop == nullptr || loop->CanDoFieldCopyProp());
  83. return true;
  84. }
  85. return loop != nullptr && loop->CanDoFieldCopyProp();
  86. }
  87. bool GlobOpt::DoFieldPRE() const
  88. {
  89. Loop *loop = this->currentBlock->loop;
  90. return DoFieldPRE(loop);
  91. }
  92. bool
  93. GlobOpt::DoFieldPRE(Loop *loop) const
  94. {
  95. if (PHASE_OFF(Js::FieldPREPhase, this->func))
  96. {
  97. return false;
  98. }
  99. if (PHASE_FORCE(Js::FieldPREPhase, func))
  100. {
  101. // Force always turns on field PRE
  102. return true;
  103. }
  104. if (this->func->HasProfileInfo() && this->func->GetReadOnlyProfileInfo()->IsFieldPREDisabled())
  105. {
  106. return false;
  107. }
  108. return DoFieldOpts(loop);
  109. }
  110. bool GlobOpt::HasMemOp(Loop *loop)
  111. {
  112. #pragma prefast(suppress: 6285, "logical-or of constants is by design")
  113. return (
  114. loop &&
  115. loop->doMemOp &&
  116. (
  117. !PHASE_OFF(Js::MemSetPhase, this->func) ||
  118. !PHASE_OFF(Js::MemCopyPhase, this->func)
  119. ) &&
  120. loop->memOpInfo &&
  121. loop->memOpInfo->candidates &&
  122. !loop->memOpInfo->candidates->Empty()
  123. );
  124. }
  125. void
  126. GlobOpt::KillLiveFields(StackSym * stackSym, BVSparse<JitArenaAllocator> * bv)
  127. {
  128. if (stackSym->IsTypeSpec())
  129. {
  130. stackSym = stackSym->GetVarEquivSym(this->func);
  131. }
  132. Assert(stackSym);
  133. // If the sym has no objectSymInfo, it must not represent an object and, hence, has no type sym or
  134. // property syms to kill.
  135. if (!stackSym->HasObjectInfo() || stackSym->IsSingleDef())
  136. {
  137. return;
  138. }
  139. // Note that the m_writeGuardSym is killed here as well, because it is part of the
  140. // m_propertySymList of the object.
  141. ObjectSymInfo * objectSymInfo = stackSym->GetObjectInfo();
  142. PropertySym * propertySym = objectSymInfo->m_propertySymList;
  143. while (propertySym != nullptr)
  144. {
  145. Assert(propertySym->m_stackSym == stackSym);
  146. bv->Clear(propertySym->m_id);
  147. if (this->IsLoopPrePass())
  148. {
  149. for (Loop * loop = this->rootLoopPrePass; loop != nullptr; loop = loop->parent)
  150. {
  151. loop->fieldKilled->Set(propertySym->m_id);
  152. }
  153. }
  154. else if (bv->IsEmpty())
  155. {
  156. // shortcut
  157. break;
  158. }
  159. propertySym = propertySym->m_nextInStackSymList;
  160. }
  161. this->KillObjectType(stackSym, bv);
  162. }
  163. void
  164. GlobOpt::KillLiveFields(PropertySym * propertySym, BVSparse<JitArenaAllocator> * bv)
  165. {
  166. KillLiveFields(propertySym->m_propertyEquivSet, bv);
  167. }
  168. void GlobOpt::KillLiveFields(BVSparse<JitArenaAllocator> *const fieldsToKill, BVSparse<JitArenaAllocator> *const bv) const
  169. {
  170. Assert(bv);
  171. if (fieldsToKill)
  172. {
  173. bv->Minus(fieldsToKill);
  174. if (this->IsLoopPrePass())
  175. {
  176. for (Loop * loop = this->rootLoopPrePass; loop != nullptr; loop = loop->parent)
  177. {
  178. loop->fieldKilled->Or(fieldsToKill);
  179. }
  180. }
  181. }
  182. }
  183. void
  184. GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func)
  185. {
  186. // obj.x = 10;
  187. // obj["x"] = ...; // This needs to kill obj.x... We need to kill all fields...
  188. //
  189. // Also, 'arguments[i] =' needs to kill all slots even if 'i' is an int.
  190. //
  191. // NOTE: we only need to kill slots here, not all fields. It may be good to separate these one day.
  192. //
  193. // Regarding the check for type specialization:
  194. // - Type specialization does not always update the value to a definite type.
  195. // - The loop prepass is conservative on values even when type specialization occurs.
  196. // - We check the type specialization status for the sym as well. For the purpose of doing kills, we can assume that
  197. // if type specialization happened, that fields don't need to be killed. Note that they may be killed in the next
  198. // pass based on the value.
  199. if (func->GetThisOrParentInlinerHasArguments() || this->IsNonNumericRegOpnd(indirOpnd->GetIndexOpnd(), inGlobOpt))
  200. {
  201. this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms
  202. SetAnyPropertyMayBeWrittenTo();
  203. }
  204. else if (inGlobOpt && indexOpnd && !indexOpnd->GetValueType().IsInt() && !currentBlock->globOptData.IsInt32TypeSpecialized(indexOpnd->m_sym))
  205. {
  206. // Write/delete to a non-integer numeric index can't alias a name on the RHS of a dot, but it change object layout
  207. this->KillAllObjectTypes(bv);
  208. }
  209. }
  210. void
  211. GlobOpt::KillAllFields(BVSparse<JitArenaAllocator> * bv)
  212. {
  213. bv->ClearAll();
  214. if (this->IsLoopPrePass())
  215. {
  216. for (Loop * loop = this->rootLoopPrePass; loop != nullptr; loop = loop->parent)
  217. {
  218. loop->allFieldsKilled = true;
  219. }
  220. }
  221. }
  222. void
  223. GlobOpt::SetAnyPropertyMayBeWrittenTo()
  224. {
  225. this->func->anyPropertyMayBeWrittenTo = true;
  226. }
  227. void
  228. GlobOpt::AddToPropertiesWrittenTo(Js::PropertyId propertyId)
  229. {
  230. this->func->EnsurePropertiesWrittenTo();
  231. this->func->propertiesWrittenTo->Item(propertyId);
  232. }
  233. void
  234. GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bool inGlobOpt)
  235. {
  236. if (bv->IsEmpty() && (!this->IsLoopPrePass() || this->rootLoopPrePass->allFieldsKilled))
  237. {
  238. return;
  239. }
  240. if (instr->m_opcode == Js::OpCode::FromVar || instr->m_opcode == Js::OpCode::Conv_Prim)
  241. {
  242. return;
  243. }
  244. IR::Opnd * dstOpnd = instr->GetDst();
  245. if (dstOpnd)
  246. {
  247. if (dstOpnd->IsRegOpnd())
  248. {
  249. Sym * sym = dstOpnd->AsRegOpnd()->m_sym;
  250. if (sym->IsStackSym())
  251. {
  252. KillLiveFields(sym->AsStackSym(), bv);
  253. }
  254. }
  255. else if (dstOpnd->IsSymOpnd())
  256. {
  257. Sym * sym = dstOpnd->AsSymOpnd()->m_sym;
  258. if (sym->IsStackSym())
  259. {
  260. KillLiveFields(sym->AsStackSym(), bv);
  261. }
  262. else
  263. {
  264. Assert(sym->IsPropertySym());
  265. if (instr->m_opcode == Js::OpCode::InitLetFld || instr->m_opcode == Js::OpCode::InitConstFld || instr->m_opcode == Js::OpCode::InitFld)
  266. {
  267. // These can grow the aux slot of the activation object.
  268. // We need to kill the slot array sym as well.
  269. PropertySym * slotArraySym = PropertySym::Find(sym->AsPropertySym()->m_stackSym->m_id,
  270. (Js::DynamicObject::GetOffsetOfAuxSlots())/sizeof(Js::Var) /*, PropertyKindSlotArray */, instr->m_func);
  271. if (slotArraySym)
  272. {
  273. bv->Clear(slotArraySym->m_id);
  274. }
  275. }
  276. }
  277. }
  278. }
  279. if (bv->IsEmpty() && (!this->IsLoopPrePass() || this->rootLoopPrePass->allFieldsKilled))
  280. {
  281. return;
  282. }
  283. Sym *sym;
  284. IR::JnHelperMethod fnHelper;
  285. switch(instr->m_opcode)
  286. {
  287. case Js::OpCode::StElemI_A:
  288. case Js::OpCode::StElemI_A_Strict:
  289. Assert(dstOpnd != nullptr);
  290. KillLiveFields(this->lengthEquivBv, bv);
  291. KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
  292. if (inGlobOpt)
  293. {
  294. KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
  295. }
  296. break;
  297. case Js::OpCode::InitComputedProperty:
  298. case Js::OpCode::InitGetElemI:
  299. case Js::OpCode::InitSetElemI:
  300. KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
  301. if (inGlobOpt)
  302. {
  303. KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
  304. }
  305. break;
  306. case Js::OpCode::DeleteElemI_A:
  307. case Js::OpCode::DeleteElemIStrict_A:
  308. Assert(dstOpnd != nullptr);
  309. KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
  310. break;
  311. case Js::OpCode::DeleteFld:
  312. case Js::OpCode::DeleteRootFld:
  313. case Js::OpCode::DeleteFldStrict:
  314. case Js::OpCode::DeleteRootFldStrict:
  315. sym = instr->GetSrc1()->AsSymOpnd()->m_sym;
  316. KillLiveFields(sym->AsPropertySym(), bv);
  317. if (inGlobOpt)
  318. {
  319. AddToPropertiesWrittenTo(sym->AsPropertySym()->m_propertyId);
  320. this->KillAllObjectTypes(bv);
  321. }
  322. break;
  323. case Js::OpCode::InitSetFld:
  324. case Js::OpCode::InitGetFld:
  325. case Js::OpCode::InitClassMemberGet:
  326. case Js::OpCode::InitClassMemberSet:
  327. sym = instr->GetDst()->AsSymOpnd()->m_sym;
  328. KillLiveFields(sym->AsPropertySym(), bv);
  329. if (inGlobOpt)
  330. {
  331. AddToPropertiesWrittenTo(sym->AsPropertySym()->m_propertyId);
  332. this->KillAllObjectTypes(bv);
  333. }
  334. break;
  335. case Js::OpCode::InitFld:
  336. case Js::OpCode::StFld:
  337. case Js::OpCode::StRootFld:
  338. case Js::OpCode::StFldStrict:
  339. case Js::OpCode::StRootFldStrict:
  340. case Js::OpCode::StSlot:
  341. case Js::OpCode::StSlotChkUndecl:
  342. Assert(dstOpnd != nullptr);
  343. sym = dstOpnd->AsSymOpnd()->m_sym;
  344. if (inGlobOpt)
  345. {
  346. AddToPropertiesWrittenTo(sym->AsPropertySym()->m_propertyId);
  347. }
  348. if ((inGlobOpt && (sym->AsPropertySym()->m_propertyId == Js::PropertyIds::valueOf || sym->AsPropertySym()->m_propertyId == Js::PropertyIds::toString)) ||
  349. instr->CallsAccessor())
  350. {
  351. // If overriding valueof/tostring, we might have expected a previous LdFld to bailout on implicitCalls but didn't.
  352. // CSE's for example would have expected a bailout. Clear all fields to prevent optimizing across.
  353. this->KillAllFields(bv);
  354. }
  355. else
  356. {
  357. KillLiveFields(sym->AsPropertySym(), bv);
  358. }
  359. break;
  360. case Js::OpCode::InlineArrayPush:
  361. case Js::OpCode::InlineArrayPop:
  362. KillLiveFields(this->lengthEquivBv, bv);
  363. if (inGlobOpt)
  364. {
  365. // Deleting an item, or pushing a property to a non-array, may change object layout
  366. KillAllObjectTypes(bv);
  367. }
  368. break;
  369. case Js::OpCode::InlineeStart:
  370. case Js::OpCode::InlineeEnd:
  371. Assert(!instr->UsesAllFields());
  372. // Kill all live 'arguments' and 'caller' fields, as 'inlineeFunction.arguments' and 'inlineeFunction.caller'
  373. // cannot be copy-propped across different instances of the same inlined function.
  374. KillLiveFields(argumentsEquivBv, bv);
  375. KillLiveFields(callerEquivBv, bv);
  376. break;
  377. case Js::OpCode::CallDirect:
  378. fnHelper = instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper;
  379. switch (fnHelper)
  380. {
  381. case IR::JnHelperMethod::HelperArray_Shift:
  382. case IR::JnHelperMethod::HelperArray_Splice:
  383. case IR::JnHelperMethod::HelperArray_Unshift:
  384. // Kill length field for built-ins that can update it.
  385. if (nullptr != this->lengthEquivBv)
  386. {
  387. KillLiveFields(this->lengthEquivBv, bv);
  388. }
  389. // fall through
  390. case IR::JnHelperMethod::HelperArray_Reverse:
  391. // Deleting an item may change object layout
  392. if (inGlobOpt)
  393. {
  394. KillAllObjectTypes(bv);
  395. }
  396. break;
  397. case IR::JnHelperMethod::HelperRegExp_Exec:
  398. case IR::JnHelperMethod::HelperString_Match:
  399. case IR::JnHelperMethod::HelperString_Replace:
  400. // Consider: We may not need to kill all fields here.
  401. this->KillAllFields(bv);
  402. break;
  403. }
  404. break;
  405. case Js::OpCode::LdHeapArguments:
  406. case Js::OpCode::LdLetHeapArguments:
  407. case Js::OpCode::LdHeapArgsCached:
  408. case Js::OpCode::LdLetHeapArgsCached:
  409. if (inGlobOpt) {
  410. this->KillLiveFields(this->slotSyms, bv);
  411. }
  412. break;
  413. case Js::OpCode::InitClass:
  414. case Js::OpCode::InitProto:
  415. case Js::OpCode::NewScObjectNoCtor:
  416. if (inGlobOpt)
  417. {
  418. KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
  419. }
  420. break;
  421. default:
  422. if (instr->UsesAllFields())
  423. {
  424. // This also kills all property type values, as the same bit-vector tracks those stack syms.
  425. this->KillAllFields(bv);
  426. }
  427. break;
  428. }
  429. }
  430. void
  431. GlobOpt::ProcessFieldKills(IR::Instr * instr)
  432. {
  433. if (!this->DoFieldCopyProp() && !this->DoFieldRefOpts() && !DoCSE())
  434. {
  435. Assert(this->currentBlock->globOptData.liveFields->IsEmpty());
  436. return;
  437. }
  438. ProcessFieldKills(instr, this->currentBlock->globOptData.liveFields, true);
  439. }
  440. Value *
  441. GlobOpt::CreateFieldSrcValue(PropertySym * sym, PropertySym * originalSym, IR::Opnd ** ppOpnd, IR::Instr * instr)
  442. {
  443. #if DBG
  444. // If the opcode going to kill all field values immediate anyway, we shouldn't be giving it a value
  445. Assert(!instr->UsesAllFields());
  446. AssertCanCopyPropOrCSEFieldLoad(instr);
  447. Assert(instr->GetSrc1() == *ppOpnd);
  448. #endif
  449. // Only give a value to fields if we are doing field copy prop.
  450. // Consider: We should always copy prop local slots, but the only use right now is LdSlot from jit loop body.
  451. // This should have one onus load, and thus no need for copy prop of field itself. We may want to support
  452. // copy prop LdSlot if there are other uses of local slots
  453. if (!this->DoFieldCopyProp())
  454. {
  455. return nullptr;
  456. }
  457. BOOL wasLive = this->currentBlock->globOptData.liveFields->TestAndSet(sym->m_id);
  458. if (sym != originalSym)
  459. {
  460. this->currentBlock->globOptData.liveFields->TestAndSet(originalSym->m_id);
  461. }
  462. if (!wasLive)
  463. {
  464. // We don't clear the value when we kill the field.
  465. // Clear it to make sure we don't use the old value.
  466. this->currentBlock->globOptData.ClearSymValue(sym);
  467. this->currentBlock->globOptData.ClearSymValue(originalSym);
  468. }
  469. Assert((*ppOpnd)->AsSymOpnd()->m_sym == sym || this->IsLoopPrePass());
  470. // We don't use the sym store to do copy prop on hoisted fields, but create a value
  471. // in case it can be copy prop out of the loop.
  472. return this->NewGenericValue(ValueType::Uninitialized, *ppOpnd);
  473. }
  474. bool
  475. GlobOpt::NeedBailOnImplicitCallWithFieldOpts(Loop *loop, bool hasLiveFields) const
  476. {
  477. if (!(((this->DoFieldRefOpts(loop) ||
  478. this->DoFieldCopyProp(loop)) &&
  479. hasLiveFields)))
  480. {
  481. return false;
  482. }
  483. return true;
  484. }
  485. IR::Instr *
  486. GlobOpt::EnsureDisableImplicitCallRegion(Loop * loop)
  487. {
  488. Assert(loop->bailOutInfo != nullptr);
  489. IR::Instr * endDisableImplicitCall = loop->endDisableImplicitCall;
  490. if (endDisableImplicitCall)
  491. {
  492. return endDisableImplicitCall;
  493. }
  494. IR::Instr * bailOutTarget = EnsureBailTarget(loop);
  495. Func * bailOutFunc = loop->GetFunc();
  496. Assert(loop->bailOutInfo->bailOutFunc == bailOutFunc);
  497. IR::MemRefOpnd * disableImplicitCallAddress = IR::MemRefOpnd::New(this->func->GetThreadContextInfo()->GetDisableImplicitFlagsAddr(), TyInt8, bailOutFunc);
  498. IR::IntConstOpnd * disableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitCallAndExceptionFlag, TyInt8, bailOutFunc, true);
  499. IR::IntConstOpnd * enableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitNoFlag, TyInt8, bailOutFunc, true);
  500. IR::Opnd * implicitCallFlags = Lowerer::GetImplicitCallFlagsOpnd(bailOutFunc);
  501. IR::IntConstOpnd * noImplicitCall = IR::IntConstOpnd::New(Js::ImplicitCall_None, TyInt8, bailOutFunc, true);
  502. // Consider: if we are already doing implicit call in the outer loop, we don't need to clear the implicit call bit again
  503. IR::Instr * clearImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, implicitCallFlags, noImplicitCall, bailOutFunc);
  504. bailOutTarget->InsertBefore(clearImplicitCall);
  505. IR::Instr * disableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, disableImplicitCallAndExceptionValue, bailOutFunc);
  506. bailOutTarget->InsertBefore(disableImplicitCall);
  507. endDisableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, enableImplicitCallAndExceptionValue, bailOutFunc);
  508. bailOutTarget->InsertBefore(endDisableImplicitCall);
  509. IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, IR::BailOutOnImplicitCalls, loop->bailOutInfo, loop->bailOutInfo->bailOutFunc);
  510. bailOutInstr->SetSrc1(implicitCallFlags);
  511. bailOutInstr->SetSrc2(noImplicitCall);
  512. bailOutTarget->InsertBefore(bailOutInstr);
  513. loop->endDisableImplicitCall = endDisableImplicitCall;
  514. return endDisableImplicitCall;
  515. }
  516. #if DBG
  517. bool
  518. GlobOpt::IsPropertySymId(SymID symId) const
  519. {
  520. return this->func->m_symTable->Find(symId)->IsPropertySym();
  521. }
  522. void
  523. GlobOpt::AssertCanCopyPropOrCSEFieldLoad(IR::Instr * instr)
  524. {
  525. // Consider: Hoisting LdRootFld may have complication with exception if the field doesn't exist.
  526. // We need to have another opcode for the hoisted version to avoid the exception and bailout.
  527. Assert(instr->m_opcode == Js::OpCode::LdSlot || instr->m_opcode == Js::OpCode::LdSlotArr
  528. || instr->m_opcode == Js::OpCode::LdFld || instr->m_opcode == Js::OpCode::LdFldForCallApplyTarget
  529. || instr->m_opcode == Js::OpCode::LdLen_A
  530. || instr->m_opcode == Js::OpCode::LdRootFld || instr->m_opcode == Js::OpCode::LdSuperFld
  531. || instr->m_opcode == Js::OpCode::LdFldForTypeOf || instr->m_opcode == Js::OpCode::LdRootFldForTypeOf
  532. || instr->m_opcode == Js::OpCode::LdMethodFld || instr->m_opcode == Js::OpCode::LdMethodFldPolyInlineMiss
  533. || instr->m_opcode == Js::OpCode::LdRootMethodFld
  534. || instr->m_opcode == Js::OpCode::LdMethodFromFlags
  535. || instr->m_opcode == Js::OpCode::ScopedLdMethodFld
  536. || instr->m_opcode == Js::OpCode::CheckFixedFld
  537. || instr->m_opcode == Js::OpCode::CheckPropertyGuardAndLoadType
  538. || instr->m_opcode == Js::OpCode::ScopedLdFld
  539. || instr->m_opcode == Js::OpCode::ScopedLdFldForTypeOf);
  540. Assert(instr->m_opcode == Js::OpCode::CheckFixedFld || instr->GetDst()->GetType() == TyVar || instr->m_func->GetJITFunctionBody()->IsAsmJsMode());
  541. Assert(instr->GetSrc1()->GetType() == TyVar || instr->m_func->GetJITFunctionBody()->IsAsmJsMode());
  542. Assert(instr->GetSrc1()->AsSymOpnd()->m_sym->IsPropertySym());
  543. Assert(instr->GetSrc2() == nullptr);
  544. }
  545. #endif
  546. StackSym *
  547. GlobOpt::EnsureObjectTypeSym(StackSym * objectSym)
  548. {
  549. Assert(!objectSym->IsTypeSpec());
  550. objectSym->EnsureObjectInfo(this->func);
  551. if (objectSym->HasObjectTypeSym())
  552. {
  553. Assert(this->objectTypeSyms);
  554. return objectSym->GetObjectTypeSym();
  555. }
  556. if (this->objectTypeSyms == nullptr)
  557. {
  558. this->objectTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  559. }
  560. StackSym * typeSym = StackSym::New(TyVar, this->func);
  561. objectSym->GetObjectInfo()->m_typeSym = typeSym;
  562. this->objectTypeSyms->Set(typeSym->m_id);
  563. return typeSym;
  564. }
  565. PropertySym *
  566. GlobOpt::EnsurePropertyWriteGuardSym(PropertySym * propertySym)
  567. {
  568. // Make sure that the PropertySym has a proto cache sym which is chained into the propertySym list.
  569. if (!propertySym->m_writeGuardSym)
  570. {
  571. propertySym->m_writeGuardSym = PropertySym::New(propertySym->m_stackSym, propertySym->m_propertyId, (uint32)-1, (uint)-1, PropertyKindWriteGuard, this->func);
  572. }
  573. return propertySym->m_writeGuardSym;
  574. }
  575. void
  576. GlobOpt::PreparePropertySymForTypeCheckSeq(PropertySym *propertySym)
  577. {
  578. Assert(!propertySym->m_stackSym->IsTypeSpec());
  579. EnsureObjectTypeSym(propertySym->m_stackSym);
  580. EnsurePropertyWriteGuardSym(propertySym);
  581. }
  582. bool
  583. GlobOpt::IsPropertySymPreparedForTypeCheckSeq(PropertySym *propertySym)
  584. {
  585. Assert(!propertySym->m_stackSym->IsTypeSpec());
  586. // The following doesn't need to be true. We may copy prop a constant into an object sym, which has
  587. // previously been prepared for type check sequence optimization.
  588. // Assert(!propertySym->m_stackSym->m_isIntConst || !propertySym->HasObjectTypeSym());
  589. // The following doesn't need to be true. We may copy prop the object sym into a field load or store
  590. // that doesn't have object type spec info and hence the operand wasn't prepared and doesn't have a write
  591. // guard. The object sym, however, may have other field operations which are object type specialized and
  592. // thus the type sym for it has been created.
  593. // Assert(propertySym->HasObjectTypeSym() == propertySym->HasWriteGuardSym());
  594. return propertySym->HasObjectTypeSym();
  595. }
  596. bool
  597. GlobOpt::PreparePropertySymOpndForTypeCheckSeq(IR::PropertySymOpnd * propertySymOpnd, IR::Instr* instr, Loop * loop)
  598. {
  599. if (!DoFieldRefOpts(loop) || !OpCodeAttr::FastFldInstr(instr->m_opcode) || instr->CallsAccessor())
  600. {
  601. return false;
  602. }
  603. if (!propertySymOpnd->HasObjTypeSpecFldInfo())
  604. {
  605. return false;
  606. }
  607. ObjTypeSpecFldInfo* info = propertySymOpnd->GetObjTypeSpecInfo();
  608. if (info->UsesAccessor() || info->IsRootObjectNonConfigurableFieldLoad())
  609. {
  610. return false;
  611. }
  612. if (info->IsPoly() && !info->GetEquivalentTypeSet())
  613. {
  614. return false;
  615. }
  616. PropertySym * propertySym = propertySymOpnd->m_sym->AsPropertySym();
  617. PreparePropertySymForTypeCheckSeq(propertySym);
  618. propertySymOpnd->SetTypeCheckSeqCandidate(true);
  619. propertySymOpnd->SetIsBeingStored(propertySymOpnd == instr->GetDst());
  620. return true;
  621. }
  622. bool
  623. GlobOpt::CheckIfPropOpEmitsTypeCheck(IR::Instr *instr, IR::PropertySymOpnd *opnd)
  624. {
  625. if (!DoFieldRefOpts() || !OpCodeAttr::FastFldInstr(instr->m_opcode))
  626. {
  627. return false;
  628. }
  629. if (!opnd->IsTypeCheckSeqCandidate())
  630. {
  631. return false;
  632. }
  633. return CheckIfInstrInTypeCheckSeqEmitsTypeCheck(instr, opnd);
  634. }
  635. IR::PropertySymOpnd *
  636. GlobOpt::CreateOpndForTypeCheckOnly(IR::PropertySymOpnd* opnd, Func* func)
  637. {
  638. // Used only for CheckObjType instruction today. Future users should make a call
  639. // whether the new operand is jit optimized in their scenario or not.
  640. Assert(!opnd->IsRootObjectNonConfigurableFieldLoad());
  641. IR::PropertySymOpnd *newOpnd = opnd->CopyCommon(func);
  642. newOpnd->SetObjTypeSpecFldInfo(opnd->GetObjTypeSpecInfo());
  643. newOpnd->SetUsesAuxSlot(opnd->UsesAuxSlot());
  644. newOpnd->SetSlotIndex(opnd->GetSlotIndex());
  645. newOpnd->objTypeSpecFlags = opnd->objTypeSpecFlags;
  646. // If we're turning the instruction owning this operand into a CheckObjType, we will do a type check here
  647. // only for the sake of downstream instructions, so the flags pertaining to this property access are
  648. // irrelevant, because we don't do a property access here.
  649. newOpnd->SetTypeCheckOnly(true);
  650. newOpnd->usesFixedValue = false;
  651. newOpnd->finalType = opnd->finalType;
  652. newOpnd->guardedPropOps = opnd->guardedPropOps != nullptr ? opnd->guardedPropOps->CopyNew() : nullptr;
  653. newOpnd->writeGuards = opnd->writeGuards != nullptr ? opnd->writeGuards->CopyNew() : nullptr;
  654. newOpnd->SetIsJITOptimizedReg(true);
  655. return newOpnd;
  656. }
  657. bool
  658. GlobOpt::FinishOptPropOp(IR::Instr *instr, IR::PropertySymOpnd *opnd, BasicBlock* block, bool updateExistingValue, bool* emitsTypeCheckOut, bool* changesTypeValueOut)
  659. {
  660. if (!DoFieldRefOpts() || !OpCodeAttr::FastFldInstr(instr->m_opcode))
  661. {
  662. return false;
  663. }
  664. bool isTypeCheckSeqCandidate = opnd->IsTypeCheckSeqCandidate();
  665. bool isObjTypeSpecialized = false;
  666. bool isObjTypeChecked = false;
  667. if (isTypeCheckSeqCandidate)
  668. {
  669. isObjTypeSpecialized = ProcessPropOpInTypeCheckSeq<true>(instr, opnd, block, updateExistingValue, emitsTypeCheckOut, changesTypeValueOut, &isObjTypeChecked);
  670. }
  671. if (opnd == instr->GetDst() && this->objectTypeSyms)
  672. {
  673. if (block == nullptr)
  674. {
  675. block = this->currentBlock;
  676. }
  677. // This is a property store that may change the layout of the object that it stores to. This means that
  678. // it may change any aliased object. Do two things to address this:
  679. // - Add all object types in this function to the set that may have had a property added. This will prevent
  680. // final type optimization across this instruction. (Only needed here for non-specialized stores.)
  681. // - Kill all type symbols that currently hold object-header-inlined types. Any of them may have their layout
  682. // changed by the addition of a property.
  683. SymID opndId = opnd->HasObjectTypeSym() ? opnd->GetObjectTypeSym()->m_id : -1;
  684. if (!isObjTypeChecked)
  685. {
  686. if (block->globOptData.maybeWrittenTypeSyms == nullptr)
  687. {
  688. block->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  689. }
  690. if (isObjTypeSpecialized)
  691. {
  692. // The current object will be protected by a type check, unless no further accesses to it are
  693. // protected by this access.
  694. Assert(this->objectTypeSyms->Test(opndId));
  695. this->objectTypeSyms->Clear(opndId);
  696. }
  697. block->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
  698. if (isObjTypeSpecialized)
  699. {
  700. this->objectTypeSyms->Set(opndId);
  701. }
  702. }
  703. if (!isObjTypeSpecialized || opnd->ChangesObjectLayout())
  704. {
  705. this->KillObjectHeaderInlinedTypeSyms(block, isObjTypeSpecialized, opndId);
  706. this->KillAuxSlotPtrSyms(opnd, block, isObjTypeSpecialized);
  707. }
  708. else if (!isObjTypeChecked && this->HasLiveObjectHeaderInlinedTypeSym(block, true, opndId))
  709. {
  710. opnd->SetTypeCheckRequired(true);
  711. }
  712. }
  713. return isObjTypeSpecialized;
  714. }
  715. StackSym *
  716. GlobOpt::EnsureAuxSlotPtrSym(IR::PropertySymOpnd *opnd)
  717. {
  718. StackSym *auxSlotPtrSym = opnd->EnsureAuxSlotPtrSym(this->func);
  719. this->auxSlotPtrSyms->Set(auxSlotPtrSym->m_id);
  720. return auxSlotPtrSym;
  721. }
  722. void
  723. GlobOpt::KillAuxSlotPtrSyms(IR::PropertySymOpnd *opnd, BasicBlock *block, bool isObjTypeSpecialized)
  724. {
  725. StackSym *auxSlotPtrSym = nullptr;
  726. if (isObjTypeSpecialized)
  727. {
  728. // Kill all aux slot syms other than this one
  729. auxSlotPtrSym = opnd->GetAuxSlotPtrSym();
  730. if (auxSlotPtrSym)
  731. {
  732. Assert(this->auxSlotPtrSyms && this->auxSlotPtrSyms->Test(auxSlotPtrSym->m_id));
  733. this->auxSlotPtrSyms->Clear(auxSlotPtrSym->m_id);
  734. }
  735. }
  736. block->globOptData.liveFields->Minus(this->auxSlotPtrSyms);
  737. if (auxSlotPtrSym)
  738. {
  739. this->auxSlotPtrSyms->Set(auxSlotPtrSym->m_id);
  740. }
  741. }
  742. void
  743. GlobOpt::KillObjectHeaderInlinedTypeSyms(BasicBlock *block, bool isObjTypeSpecialized, SymID opndId)
  744. {
  745. this->MapObjectHeaderInlinedTypeSymsUntil(block, isObjTypeSpecialized, opndId, [&](SymID symId)->bool { this->currentBlock->globOptData.liveFields->Clear(symId); return false; });
  746. }
  747. bool
  748. GlobOpt::HasLiveObjectHeaderInlinedTypeSym(BasicBlock *block, bool isObjTypeSpecialized, SymID opndId)
  749. {
  750. return this->MapObjectHeaderInlinedTypeSymsUntil(block, true, opndId, [&](SymID symId)->bool { return this->currentBlock->globOptData.liveFields->Test(symId); });
  751. }
  752. template<class Fn>
  753. bool
  754. GlobOpt::MapObjectHeaderInlinedTypeSymsUntil(BasicBlock *block, bool isObjTypeSpecialized, SymID opndId, Fn fn)
  755. {
  756. if (this->objectTypeSyms == nullptr)
  757. {
  758. return false;
  759. }
  760. FOREACH_BITSET_IN_SPARSEBV(symId, this->objectTypeSyms)
  761. {
  762. if (symId == opndId && isObjTypeSpecialized)
  763. {
  764. // The current object will be protected by a type check, unless no further accesses to it are
  765. // protected by this access.
  766. continue;
  767. }
  768. Value *value = block->globOptData.FindObjectTypeValue(symId);
  769. if (value)
  770. {
  771. JsTypeValueInfo *valueInfo = value->GetValueInfo()->AsJsType();
  772. Assert(valueInfo);
  773. if (valueInfo->GetJsType() != nullptr)
  774. {
  775. JITTypeHolder type(valueInfo->GetJsType());
  776. if (Js::DynamicType::Is(type->GetTypeId()))
  777. {
  778. if (type->GetTypeHandler()->IsObjectHeaderInlinedTypeHandler())
  779. {
  780. if (fn(symId))
  781. {
  782. return true;
  783. }
  784. }
  785. }
  786. }
  787. else if (valueInfo->GetJsTypeSet())
  788. {
  789. Js::EquivalentTypeSet *typeSet = valueInfo->GetJsTypeSet();
  790. for (uint16 i = 0; i < typeSet->GetCount(); i++)
  791. {
  792. JITTypeHolder type = typeSet->GetType(i);
  793. if (type != nullptr && Js::DynamicType::Is(type->GetTypeId()))
  794. {
  795. if (type->GetTypeHandler()->IsObjectHeaderInlinedTypeHandler())
  796. {
  797. if (fn(symId))
  798. {
  799. return true;
  800. }
  801. break;
  802. }
  803. }
  804. }
  805. }
  806. }
  807. }
  808. NEXT_BITSET_IN_SPARSEBV;
  809. return false;
  810. }
  811. bool
  812. GlobOpt::AreTypeSetsIdentical(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet * rightTypeSet)
  813. {
  814. return Js::EquivalentTypeSet::AreIdentical(leftTypeSet, rightTypeSet);
  815. }
  816. bool
  817. GlobOpt::IsSubsetOf(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet * rightTypeSet)
  818. {
  819. return Js::EquivalentTypeSet::IsSubsetOf(leftTypeSet, rightTypeSet);
  820. }
  821. bool
  822. GlobOpt::CompareCurrentTypesWithExpectedTypes(JsTypeValueInfo *valueInfo, IR::PropertySymOpnd * propertySymOpnd)
  823. {
  824. bool isTypeDead = propertySymOpnd->IsTypeDead();
  825. if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
  826. {
  827. // No upstream types. Do a type check.
  828. return !isTypeDead;
  829. }
  830. if (!propertySymOpnd->HasEquivalentTypeSet() || propertySymOpnd->NeedsMonoCheck())
  831. {
  832. JITTypeHolder opndType = propertySymOpnd->GetType();
  833. if (valueInfo->GetJsType() != nullptr)
  834. {
  835. if (valueInfo->GetJsType() == propertySymOpnd->GetType())
  836. {
  837. return true;
  838. }
  839. if (propertySymOpnd->HasInitialType() && valueInfo->GetJsType() == propertySymOpnd->GetInitialType())
  840. {
  841. return !isTypeDead;
  842. }
  843. return false;
  844. }
  845. else
  846. {
  847. Assert(valueInfo->GetJsTypeSet());
  848. Js::EquivalentTypeSet *valueTypeSet = valueInfo->GetJsTypeSet();
  849. if (valueTypeSet->Contains(opndType))
  850. {
  851. return !isTypeDead;
  852. }
  853. if (propertySymOpnd->HasInitialType() && valueTypeSet->Contains(propertySymOpnd->GetInitialType()))
  854. {
  855. return !isTypeDead;
  856. }
  857. return false;
  858. }
  859. }
  860. else
  861. {
  862. Js::EquivalentTypeSet * opndTypeSet = propertySymOpnd->GetEquivalentTypeSet();
  863. if (valueInfo->GetJsType() != nullptr)
  864. {
  865. uint16 checkedTypeSetIndex;
  866. if (opndTypeSet->Contains(valueInfo->GetJsType(), &checkedTypeSetIndex))
  867. {
  868. return true;
  869. }
  870. return false;
  871. }
  872. else
  873. {
  874. if (IsSubsetOf(valueInfo->GetJsTypeSet(), opndTypeSet))
  875. {
  876. return true;
  877. }
  878. if (propertySymOpnd->IsMono() ?
  879. valueInfo->GetJsTypeSet()->Contains(propertySymOpnd->GetFirstEquivalentType()) :
  880. IsSubsetOf(opndTypeSet, valueInfo->GetJsTypeSet()))
  881. {
  882. return true;
  883. }
  884. return false;
  885. }
  886. }
  887. }
  888. bool
  889. GlobOpt::ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd)
  890. {
  891. return ProcessPropOpInTypeCheckSeq<true>(instr, opnd, this->currentBlock, false);
  892. }
  893. bool GlobOpt::CheckIfInstrInTypeCheckSeqEmitsTypeCheck(IR::Instr* instr, IR::PropertySymOpnd *opnd)
  894. {
  895. bool emitsTypeCheck;
  896. ProcessPropOpInTypeCheckSeq<false>(instr, opnd, this->currentBlock, false, &emitsTypeCheck);
  897. return emitsTypeCheck;
  898. }
  899. template<bool makeChanges>
  900. bool
  901. GlobOpt::ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd, BasicBlock* block, bool updateExistingValue, bool* emitsTypeCheckOut, bool* changesTypeValueOut, bool *isTypeCheckedOut)
  902. {
  903. // We no longer mark types as dead in the backward pass, so we should never see an instr with a dead type here
  904. // during the forward pass. For the time being we've retained the logic below to deal with dead types in case
  905. // we ever wanted to revert back to more aggressive type killing that we had before.
  906. Assert(!opnd->IsTypeDead());
  907. Assert(opnd->IsTypeCheckSeqCandidate());
  908. Assert(opnd->HasObjectTypeSym());
  909. bool isStore = opnd == instr->GetDst();
  910. bool isTypeDead = opnd->IsTypeDead();
  911. bool consumeType = makeChanges && !IsLoopPrePass();
  912. bool produceType = makeChanges && !isTypeDead;
  913. bool isSpecialized = false;
  914. bool emitsTypeCheck = false;
  915. bool addsProperty = false;
  916. if (block == nullptr)
  917. {
  918. block = this->currentBlock;
  919. }
  920. StackSym * typeSym = opnd->GetObjectTypeSym();
  921. #if DBG
  922. uint16 typeCheckSeqFlagsBefore;
  923. Value* valueBefore = nullptr;
  924. JsTypeValueInfo* valueInfoBefore = nullptr;
  925. if (!makeChanges)
  926. {
  927. typeCheckSeqFlagsBefore = opnd->GetTypeCheckSeqFlags();
  928. valueBefore = block->globOptData.FindObjectTypeValue(typeSym);
  929. if (valueBefore != nullptr)
  930. {
  931. Assert(valueBefore->GetValueInfo() != nullptr && valueBefore->GetValueInfo()->IsJsType());
  932. valueInfoBefore = valueBefore->GetValueInfo()->AsJsType();
  933. }
  934. }
  935. #endif
  936. Value *value = block->globOptData.FindObjectTypeValue(typeSym);
  937. JsTypeValueInfo* valueInfo = value != nullptr ? value->GetValueInfo()->AsJsType() : nullptr;
  938. if (consumeType && valueInfo != nullptr)
  939. {
  940. opnd->SetTypeAvailable(true);
  941. }
  942. bool doEquivTypeCheck = opnd->HasEquivalentTypeSet() && !opnd->NeedsMonoCheck();
  943. if (!doEquivTypeCheck)
  944. {
  945. AssertOrFailFast(!opnd->NeedsDepolymorphication());
  946. // We need a monomorphic type check here (e.g., final type opt, fixed field check on non-proto property).
  947. JITTypeHolder opndType = opnd->GetType();
  948. if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
  949. {
  950. // This is the initial type check.
  951. opnd->SetTypeAvailable(false);
  952. isSpecialized = !isTypeDead;
  953. emitsTypeCheck = isSpecialized;
  954. addsProperty = isStore && isSpecialized && opnd->HasInitialType();
  955. if (produceType)
  956. {
  957. SetObjectTypeFromTypeSym(typeSym, opndType, nullptr, block, updateExistingValue);
  958. }
  959. }
  960. else if (valueInfo->GetJsType() != nullptr)
  961. {
  962. // We have a monomorphic type check upstream. Check against initial/final type.
  963. const JITTypeHolder valueType(valueInfo->GetJsType());
  964. if (valueType == opndType)
  965. {
  966. // The type on this instruction matches the live value in the value table, so there is no need to
  967. // refresh the value table.
  968. isSpecialized = true;
  969. if (isTypeCheckedOut)
  970. {
  971. *isTypeCheckedOut = true;
  972. }
  973. if (consumeType)
  974. {
  975. opnd->SetTypeChecked(true);
  976. }
  977. }
  978. else if (opnd->HasInitialType() && valueType == opnd->GetInitialType())
  979. {
  980. // Checked type matches the initial type at this store.
  981. bool objectMayHaveAcquiredAdditionalProperties =
  982. block->globOptData.maybeWrittenTypeSyms &&
  983. block->globOptData.maybeWrittenTypeSyms->Test(typeSym->m_id);
  984. if (consumeType)
  985. {
  986. opnd->SetTypeChecked(!objectMayHaveAcquiredAdditionalProperties);
  987. opnd->SetInitialTypeChecked(!objectMayHaveAcquiredAdditionalProperties);
  988. }
  989. if (produceType)
  990. {
  991. SetObjectTypeFromTypeSym(typeSym, opndType, nullptr, block, updateExistingValue);
  992. }
  993. isSpecialized = !isTypeDead || !objectMayHaveAcquiredAdditionalProperties;
  994. emitsTypeCheck = isSpecialized && objectMayHaveAcquiredAdditionalProperties;
  995. addsProperty = isSpecialized;
  996. if (isTypeCheckedOut)
  997. {
  998. *isTypeCheckedOut = !objectMayHaveAcquiredAdditionalProperties;
  999. }
  1000. }
  1001. else
  1002. {
  1003. // This must be a type mismatch situation, because the value is available, but doesn't match either
  1004. // the current type or the initial type. We will not optimize this instruction and we do not produce
  1005. // a new type value here.
  1006. isSpecialized = false;
  1007. if (consumeType)
  1008. {
  1009. opnd->SetTypeMismatch(true);
  1010. }
  1011. }
  1012. }
  1013. else
  1014. {
  1015. // We have an equivalent type check upstream, but we require a particular type at this point. We
  1016. // can't treat it as "checked", but we may benefit from checking for the required type.
  1017. Assert(valueInfo->GetJsTypeSet());
  1018. Js::EquivalentTypeSet *valueTypeSet = valueInfo->GetJsTypeSet();
  1019. if (valueTypeSet->Contains(opndType))
  1020. {
  1021. // Required type is in the type set we've checked. Check for the required type here, and
  1022. // note in the value info that we've narrowed down to this type. (But leave the type set in the
  1023. // value info so it can be merged with the same type set on other paths.)
  1024. isSpecialized = !isTypeDead;
  1025. emitsTypeCheck = isSpecialized;
  1026. if (produceType)
  1027. {
  1028. SetSingleTypeOnObjectTypeValue(value, opndType);
  1029. }
  1030. }
  1031. else if (opnd->HasInitialType() && valueTypeSet->Contains(opnd->GetInitialType()))
  1032. {
  1033. // Required initial type is in the type set we've checked. Check for the initial type here, and
  1034. // note in the value info that we've narrowed down to this type. (But leave the type set in the
  1035. // value info so it can be merged with the same type set on other paths.)
  1036. isSpecialized = !isTypeDead;
  1037. emitsTypeCheck = isSpecialized;
  1038. addsProperty = isSpecialized;
  1039. if (produceType)
  1040. {
  1041. SetSingleTypeOnObjectTypeValue(value, opndType);
  1042. }
  1043. }
  1044. else
  1045. {
  1046. // This must be a type mismatch situation, because the value is available, but doesn't match either
  1047. // the current type or the initial type. We will not optimize this instruction and we do not produce
  1048. // a new type value here.
  1049. isSpecialized = false;
  1050. if (consumeType)
  1051. {
  1052. opnd->SetTypeMismatch(true);
  1053. }
  1054. }
  1055. }
  1056. }
  1057. else
  1058. {
  1059. Assert(!opnd->NeedsMonoCheck());
  1060. Js::EquivalentTypeSet * opndTypeSet = opnd->GetEquivalentTypeSet();
  1061. uint16 checkedTypeSetIndex = (uint16)-1;
  1062. if (opnd->NeedsDepolymorphication())
  1063. {
  1064. // The opnd's type set (opndTypeSet) is non-equivalent. Test all the types coming from the valueInfo.
  1065. // If all of them are contained in opndTypeSet, and all of them have the same slot index in opnd's
  1066. // objtypespecfldinfo, then we can use that slot index and treat the set as equivalent.
  1067. // (Also test whether all types do/don't use aux slots.)
  1068. uint16 slotIndex = Js::Constants::NoSlot;
  1069. bool auxSlot = false;
  1070. // Do this work only if there is an upstream type value. We don't attempt to do a type check based on
  1071. // a non-equivalent set.
  1072. if (valueInfo != nullptr)
  1073. {
  1074. if (valueInfo->GetJsType() != nullptr)
  1075. {
  1076. opnd->TryDepolymorphication(valueInfo->GetJsType(), Js::Constants::NoSlot, false, &slotIndex, &auxSlot, &checkedTypeSetIndex);
  1077. }
  1078. else if (valueInfo->GetJsTypeSet() != nullptr)
  1079. {
  1080. Js::EquivalentTypeSet *typeSet = valueInfo->GetJsTypeSet();
  1081. for (uint16 i = 0; i < typeSet->GetCount(); i++)
  1082. {
  1083. opnd->TryDepolymorphication(typeSet->GetType(i), slotIndex, auxSlot, &slotIndex, &auxSlot);
  1084. if (slotIndex == Js::Constants::NoSlot)
  1085. {
  1086. // Indicates failure/mismatch. We're done.
  1087. break;
  1088. }
  1089. }
  1090. }
  1091. }
  1092. if (slotIndex == Js::Constants::NoSlot)
  1093. {
  1094. // Indicates failure/mismatch
  1095. isSpecialized = false;
  1096. if (consumeType)
  1097. {
  1098. opnd->SetTypeMismatch(true);
  1099. }
  1100. }
  1101. else
  1102. {
  1103. // Indicates we can optimize, as all upstream types are equivalent here.
  1104. opnd->SetSlotIndex(slotIndex);
  1105. opnd->SetUsesAuxSlot(auxSlot);
  1106. opnd->GetObjTypeSpecInfo()->SetSlotIndex(slotIndex);
  1107. opnd->GetObjTypeSpecInfo()->SetUsesAuxSlot(auxSlot);
  1108. isSpecialized = true;
  1109. if (isTypeCheckedOut)
  1110. {
  1111. *isTypeCheckedOut = true;
  1112. }
  1113. if (consumeType)
  1114. {
  1115. opnd->SetTypeChecked(true);
  1116. }
  1117. if (checkedTypeSetIndex != (uint16)-1)
  1118. {
  1119. opnd->SetCheckedTypeSetIndex(checkedTypeSetIndex);
  1120. }
  1121. }
  1122. }
  1123. else if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
  1124. {
  1125. // 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.
  1126. if (produceType)
  1127. {
  1128. if (opnd->IsMono())
  1129. {
  1130. SetObjectTypeFromTypeSym(typeSym, opnd->GetFirstEquivalentType(), nullptr, block, updateExistingValue);
  1131. }
  1132. else
  1133. {
  1134. SetObjectTypeFromTypeSym(typeSym, nullptr, opndTypeSet, block, updateExistingValue);
  1135. }
  1136. }
  1137. isSpecialized = !isTypeDead;
  1138. emitsTypeCheck = isSpecialized;
  1139. }
  1140. else if (valueInfo->GetJsType() != nullptr ?
  1141. opndTypeSet->Contains(valueInfo->GetJsType(), &checkedTypeSetIndex) :
  1142. IsSubsetOf(valueInfo->GetJsTypeSet(), opndTypeSet))
  1143. {
  1144. // All the types in the value info are contained in the set required by this access,
  1145. // meaning that they're equivalent to the opnd's type set.
  1146. // We won't have a type check, and we don't need to touch the type value.
  1147. isSpecialized = true;
  1148. if (isTypeCheckedOut)
  1149. {
  1150. *isTypeCheckedOut = true;
  1151. }
  1152. if (consumeType)
  1153. {
  1154. opnd->SetTypeChecked(true);
  1155. }
  1156. if (checkedTypeSetIndex != (uint16)-1)
  1157. {
  1158. opnd->SetCheckedTypeSetIndex(checkedTypeSetIndex);
  1159. }
  1160. }
  1161. else if (valueInfo->GetJsTypeSet() &&
  1162. (opnd->IsMono() ?
  1163. valueInfo->GetJsTypeSet()->Contains(opnd->GetFirstEquivalentType()) :
  1164. IsSubsetOf(opndTypeSet, valueInfo->GetJsTypeSet())
  1165. )
  1166. )
  1167. {
  1168. // We have an equivalent type check upstream, but we require a tighter type check at this point.
  1169. // We can't treat the operand as "checked", but check for equivalence with the tighter set and update the
  1170. // value info.
  1171. if (produceType)
  1172. {
  1173. if (opnd->IsMono())
  1174. {
  1175. SetObjectTypeFromTypeSym(typeSym, opnd->GetFirstEquivalentType(), nullptr, block, updateExistingValue);
  1176. }
  1177. else
  1178. {
  1179. SetObjectTypeFromTypeSym(typeSym, nullptr, opndTypeSet, block, updateExistingValue);
  1180. }
  1181. }
  1182. isSpecialized = !isTypeDead;
  1183. emitsTypeCheck = isSpecialized;
  1184. }
  1185. else
  1186. {
  1187. // This must be a type mismatch situation, because the value is available, but doesn't match either
  1188. // the current type or the initial type. We will not optimize this instruction and we do not produce
  1189. // a new type value here.
  1190. isSpecialized = false;
  1191. if (consumeType)
  1192. {
  1193. opnd->SetTypeMismatch(true);
  1194. }
  1195. }
  1196. }
  1197. Assert(isSpecialized || (!emitsTypeCheck && !addsProperty));
  1198. if (consumeType && opnd->MayNeedWriteGuardProtection())
  1199. {
  1200. Assert(!isStore);
  1201. PropertySym *propertySym = opnd->m_sym->AsPropertySym();
  1202. Assert(propertySym->m_writeGuardSym);
  1203. opnd->SetWriteGuardChecked(!!block->globOptData.liveFields->Test(propertySym->m_writeGuardSym->m_id));
  1204. }
  1205. // Even specialized property adds must kill all types for other property adds. That's because any other object sym
  1206. // may, in fact, be an alias of the instance whose type is being modified here. (see Windows Blue Bug 541876)
  1207. if (makeChanges && addsProperty)
  1208. {
  1209. Assert(isStore && isSpecialized);
  1210. Assert(this->objectTypeSyms != nullptr);
  1211. Assert(this->objectTypeSyms->Test(typeSym->m_id));
  1212. if (block->globOptData.maybeWrittenTypeSyms == nullptr)
  1213. {
  1214. block->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
  1215. }
  1216. this->objectTypeSyms->Clear(typeSym->m_id);
  1217. block->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
  1218. this->objectTypeSyms->Set(typeSym->m_id);
  1219. }
  1220. if (produceType && emitsTypeCheck && opnd->IsMono())
  1221. {
  1222. // Consider (ObjTypeSpec): Represent maybeWrittenTypeSyms as a flag on value info of the type sym.
  1223. if (block->globOptData.maybeWrittenTypeSyms != nullptr)
  1224. {
  1225. // We're doing a type check here, so objtypespec of property adds is safe for this type
  1226. // from this point forward.
  1227. block->globOptData.maybeWrittenTypeSyms->Clear(typeSym->m_id);
  1228. }
  1229. }
  1230. // Consider (ObjTypeSpec): Enable setting write guards live on instructions hoisted out of loops. Note that produceType
  1231. // is false if the type values on loop back edges don't match (see earlier comments).
  1232. // This means that hoisted instructions won't set write guards live if the type changes in the loop, even if
  1233. // the corresponding properties have not been written inside the loop. This may result in some unnecessary type
  1234. // checks and bailouts inside the loop. To enable this, we would need to verify the write guards are still live
  1235. // on the back edge (much like we're doing for types above).
  1236. // Consider (ObjTypeSpec): Support polymorphic write guards as well. We can't currently distinguish between mono and
  1237. // poly write guards, and a type check can only protect operations matching with respect to polymorphism (see
  1238. // BackwardPass::TrackObjTypeSpecProperties for details), so for now we only target monomorphic operations.
  1239. if (produceType && emitsTypeCheck && opnd->IsMono())
  1240. {
  1241. // If the type check we'll emit here protects some property operations that require a write guard (i.e.
  1242. // they must do an extra type check and property guard check, if they have been written to in this
  1243. // function), let's mark the write guards as live here, so we can accurately track if their properties
  1244. // have been written to. Make sure we only set those that we'll actually guard, i.e. those that match
  1245. // with respect to polymorphism.
  1246. if (opnd->GetWriteGuards() != nullptr)
  1247. {
  1248. block->globOptData.liveFields->Or(opnd->GetWriteGuards());
  1249. }
  1250. }
  1251. if (makeChanges && isTypeDead)
  1252. {
  1253. this->KillObjectType(opnd->GetObjectSym(), block->globOptData.liveFields);
  1254. }
  1255. #if DBG
  1256. if (!makeChanges)
  1257. {
  1258. uint16 typeCheckSeqFlagsAfter = opnd->GetTypeCheckSeqFlags();
  1259. Assert(typeCheckSeqFlagsBefore == typeCheckSeqFlagsAfter);
  1260. Value* valueAfter = block->globOptData.FindObjectTypeValue(typeSym);
  1261. Assert(valueBefore == valueAfter);
  1262. if (valueAfter != nullptr)
  1263. {
  1264. Assert(valueBefore != nullptr);
  1265. Assert(valueAfter->GetValueInfo() != nullptr && valueAfter->GetValueInfo()->IsJsType());
  1266. JsTypeValueInfo* valueInfoAfter = valueAfter->GetValueInfo()->AsJsType();
  1267. Assert(valueInfoBefore == valueInfoAfter);
  1268. Assert(valueInfoBefore->GetJsType() == valueInfoAfter->GetJsType());
  1269. Assert(valueInfoBefore->GetJsTypeSet() == valueInfoAfter->GetJsTypeSet());
  1270. }
  1271. }
  1272. #endif
  1273. if (emitsTypeCheckOut != nullptr)
  1274. {
  1275. *emitsTypeCheckOut = emitsTypeCheck;
  1276. }
  1277. if (changesTypeValueOut != nullptr)
  1278. {
  1279. *changesTypeValueOut = isSpecialized && (emitsTypeCheck || addsProperty);
  1280. }
  1281. if (makeChanges)
  1282. {
  1283. // Track liveness of aux slot ptr syms.
  1284. if (!PHASE_OFF(Js::ReuseAuxSlotPtrPhase, this->func) && isSpecialized)
  1285. {
  1286. if (opnd->UsesAuxSlot() && !opnd->IsLoadedFromProto())
  1287. {
  1288. // Optimized ld/st that loads/uses an aux slot ptr.
  1289. // Aux slot sym is live forward.
  1290. StackSym *auxSlotPtrSym = this->EnsureAuxSlotPtrSym(opnd);
  1291. if (!this->IsLoopPrePass() && opnd->IsTypeChecked())
  1292. {
  1293. if (block->globOptData.liveFields->TestAndSet(auxSlotPtrSym->m_id))
  1294. {
  1295. // Aux slot sym is available here. Tell lowerer to use it.
  1296. opnd->SetAuxSlotPtrSymAvailable(true);
  1297. }
  1298. }
  1299. else
  1300. {
  1301. block->globOptData.liveFields->Set(auxSlotPtrSym->m_id);
  1302. }
  1303. }
  1304. else if (!opnd->IsTypeChecked())
  1305. {
  1306. // Type sym is not available here (i.e., object shape is not known) and we're not loading the aux slots.
  1307. // May get here with aux slot sym still in live set if type sym is not in the value table.
  1308. // Clear the aux slot sym out of the live set.
  1309. StackSym *auxSlotPtrSym = opnd->GetAuxSlotPtrSym();
  1310. if (auxSlotPtrSym)
  1311. {
  1312. block->globOptData.liveFields->Clear(auxSlotPtrSym->m_id);
  1313. }
  1314. }
  1315. }
  1316. }
  1317. return isSpecialized;
  1318. }
  1319. void
  1320. GlobOpt::OptNewScObject(IR::Instr** instrPtr, Value* srcVal)
  1321. {
  1322. IR::Instr *&instr = *instrPtr;
  1323. if (!instr->IsNewScObjectInstr() || IsLoopPrePass() || !this->DoFieldRefOpts() || PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->func))
  1324. {
  1325. return;
  1326. }
  1327. bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor;
  1328. const JITTimeConstructorCache * ctorCache = instr->IsProfiledInstr() ?
  1329. instr->m_func->GetConstructorCache(static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId)) : nullptr;
  1330. // TODO: OOP JIT, enable assert
  1331. //Assert(ctorCache == nullptr || srcVal->GetValueInfo()->IsVarConstant() && Js::VarIs<Js::JavascriptFunction>(srcVal->GetValueInfo()->AsVarConstant()->VarValue()));
  1332. Assert(ctorCache == nullptr || !ctorCache->IsTypeFinal() || ctorCache->CtorHasNoExplicitReturnValue());
  1333. if (ctorCache != nullptr && !ctorCache->SkipNewScObject() && (isCtorInlined || ctorCache->IsTypeFinal()))
  1334. {
  1335. GenerateBailAtOperation(instrPtr, IR::BailOutFailedCtorGuardCheck);
  1336. }
  1337. }
  1338. void
  1339. GlobOpt::ValueNumberObjectType(IR::Opnd *dstOpnd, IR::Instr *instr)
  1340. {
  1341. if (!dstOpnd->IsRegOpnd())
  1342. {
  1343. return;
  1344. }
  1345. if (dstOpnd->AsRegOpnd()->m_sym->IsTypeSpec())
  1346. {
  1347. return;
  1348. }
  1349. if (instr->IsNewScObjectInstr())
  1350. {
  1351. // If we have a NewScObj* for which we have a valid constructor cache we know what type the created object will have.
  1352. // Let's produce the type value accordingly so we don't insert a type check and bailout in the constructor and
  1353. // potentially further downstream.
  1354. Assert(!PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->func) || !instr->HasBailOutInfo());
  1355. if (instr->HasBailOutInfo())
  1356. {
  1357. Assert(instr->IsProfiledInstr());
  1358. Assert(instr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck);
  1359. bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor;
  1360. JITTimeConstructorCache * ctorCache = instr->m_func->GetConstructorCache(static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId));
  1361. Assert(ctorCache != nullptr && (isCtorInlined || ctorCache->IsTypeFinal()));
  1362. StackSym* objSym = dstOpnd->AsRegOpnd()->m_sym;
  1363. StackSym* dstTypeSym = EnsureObjectTypeSym(objSym);
  1364. Assert(this->currentBlock->globOptData.FindValue(dstTypeSym) == nullptr);
  1365. SetObjectTypeFromTypeSym(dstTypeSym, ctorCache->GetType(), nullptr);
  1366. }
  1367. }
  1368. else
  1369. {
  1370. // If the dst opnd is a reg that has a type sym associated with it, then we are either killing
  1371. // the type's existing value or (in the case of a reg copy) assigning it the value of
  1372. // the src's type sym (if any). If the dst doesn't have a type sym, but the src does, let's
  1373. // give dst a new type sym and transfer the value.
  1374. Value *newValue = nullptr;
  1375. IR::Opnd * srcOpnd = instr->GetSrc1();
  1376. if (instr->m_opcode == Js::OpCode::Ld_A && srcOpnd->IsRegOpnd() &&
  1377. !srcOpnd->AsRegOpnd()->m_sym->IsTypeSpec() && srcOpnd->AsRegOpnd()->m_sym->HasObjectTypeSym())
  1378. {
  1379. StackSym *srcTypeSym = srcOpnd->AsRegOpnd()->m_sym->GetObjectTypeSym();
  1380. newValue = this->currentBlock->globOptData.FindValue(srcTypeSym);
  1381. }
  1382. if (newValue == nullptr)
  1383. {
  1384. if (dstOpnd->AsRegOpnd()->m_sym->HasObjectTypeSym())
  1385. {
  1386. StackSym * typeSym = dstOpnd->AsRegOpnd()->m_sym->GetObjectTypeSym();
  1387. this->currentBlock->globOptData.ClearSymValue(typeSym);
  1388. }
  1389. }
  1390. else
  1391. {
  1392. Assert(newValue->GetValueInfo()->IsJsType());
  1393. StackSym * typeSym;
  1394. if (!dstOpnd->AsRegOpnd()->m_sym->HasObjectTypeSym())
  1395. {
  1396. typeSym = nullptr;
  1397. }
  1398. typeSym = EnsureObjectTypeSym(dstOpnd->AsRegOpnd()->m_sym);
  1399. this->currentBlock->globOptData.SetValue(newValue, typeSym);
  1400. }
  1401. }
  1402. }
  1403. IR::Instr *
  1404. GlobOpt::SetTypeCheckBailOut(IR::Opnd *opnd, IR::Instr *instr, BailOutInfo *bailOutInfo)
  1405. {
  1406. if (this->IsLoopPrePass() || !opnd->IsSymOpnd())
  1407. {
  1408. return instr;
  1409. }
  1410. if (!opnd->AsSymOpnd()->IsPropertySymOpnd())
  1411. {
  1412. return instr;
  1413. }
  1414. IR::PropertySymOpnd * propertySymOpnd = opnd->AsPropertySymOpnd();
  1415. AssertMsg(propertySymOpnd->TypeCheckSeqBitsSetOnlyIfCandidate(), "Property sym operand optimized despite not being a candidate?");
  1416. AssertMsg(bailOutInfo == nullptr || !instr->HasBailOutInfo(), "Why are we adding new bailout info to an instruction that already has it?");
  1417. auto HandleBailout = [&](IR::BailOutKind bailOutKind)->void {
  1418. // At this point, we have a cached type that is live downstream or the type check is required
  1419. // for a fixed field load. If we can't do away with the type check, then we're going to need bailout,
  1420. // so lets add bailout info if we don't already have it.
  1421. if (!instr->HasBailOutInfo())
  1422. {
  1423. if (bailOutInfo)
  1424. {
  1425. instr = instr->ConvertToBailOutInstr(bailOutInfo, bailOutKind);
  1426. }
  1427. else
  1428. {
  1429. GenerateBailAtOperation(&instr, bailOutKind);
  1430. BailOutInfo *bailOutInfo = instr->GetBailOutInfo();
  1431. // Consider (ObjTypeSpec): If we're checking a fixed field here the bailout could be due to polymorphism or
  1432. // due to a fixed field turning non-fixed. Consider distinguishing between the two.
  1433. bailOutInfo->polymorphicCacheIndex = propertySymOpnd->m_inlineCacheIndex;
  1434. }
  1435. }
  1436. else if (instr->GetBailOutKind() == IR::BailOutMarkTempObject)
  1437. {
  1438. Assert(!bailOutInfo);
  1439. Assert(instr->GetBailOutInfo()->polymorphicCacheIndex == -1);
  1440. instr->SetBailOutKind(bailOutKind | IR::BailOutMarkTempObject);
  1441. instr->GetBailOutInfo()->polymorphicCacheIndex = propertySymOpnd->m_inlineCacheIndex;
  1442. }
  1443. else
  1444. {
  1445. Assert(bailOutKind == instr->GetBailOutKind());
  1446. }
  1447. };
  1448. bool isTypeCheckProtected;
  1449. IR::BailOutKind bailOutKind;
  1450. if (GlobOpt::NeedsTypeCheckBailOut(instr, propertySymOpnd, opnd == instr->GetDst(), &isTypeCheckProtected, &bailOutKind))
  1451. {
  1452. HandleBailout(bailOutKind);
  1453. }
  1454. else
  1455. {
  1456. if (instr->m_opcode == Js::OpCode::LdMethodFromFlags)
  1457. {
  1458. // If LdMethodFromFlags is hoisted to the top of the loop, we should share the same bailout Info.
  1459. // We don't need to do anything for LdMethodFromFlags that cannot be field hoisted.
  1460. HandleBailout(IR::BailOutFailedInlineTypeCheck);
  1461. }
  1462. else if (instr->HasBailOutInfo())
  1463. {
  1464. // If we already have a bailout info, but don't actually need it, let's remove it. This can happen if
  1465. // a CheckFixedFld added by the inliner (with bailout info) determined that the object's type has
  1466. // been checked upstream and no bailout is necessary here.
  1467. if (instr->m_opcode == Js::OpCode::CheckFixedFld)
  1468. {
  1469. AssertMsg(!PHASE_OFF(Js::FixedMethodsPhase, instr->m_func) ||
  1470. !PHASE_OFF(Js::UseFixedDataPropsPhase, instr->m_func), "CheckFixedFld with fixed method/data phase disabled?");
  1471. Assert(isTypeCheckProtected);
  1472. AssertMsg(instr->GetBailOutKind() == IR::BailOutFailedFixedFieldTypeCheck || instr->GetBailOutKind() == IR::BailOutFailedEquivalentFixedFieldTypeCheck,
  1473. "Only BailOutFailed[Equivalent]FixedFieldTypeCheck can be safely removed. Why does CheckFixedFld carry a different bailout kind?.");
  1474. instr->ClearBailOutInfo();
  1475. }
  1476. else if (propertySymOpnd->MayNeedTypeCheckProtection() && propertySymOpnd->IsTypeCheckProtected())
  1477. {
  1478. // Both the type and (if necessary) the proto object have been checked.
  1479. // We're doing a direct slot access. No possibility of bailout here (not even implicit call).
  1480. Assert(instr->GetBailOutKind() == IR::BailOutMarkTempObject);
  1481. instr->ClearBailOutInfo();
  1482. }
  1483. }
  1484. }
  1485. return instr;
  1486. }
  1487. void
  1488. GlobOpt::SetSingleTypeOnObjectTypeValue(Value* value, const JITTypeHolder type)
  1489. {
  1490. UpdateObjectTypeValue(value, type, true, nullptr, false);
  1491. }
  1492. void
  1493. GlobOpt::SetTypeSetOnObjectTypeValue(Value* value, Js::EquivalentTypeSet* typeSet)
  1494. {
  1495. UpdateObjectTypeValue(value, nullptr, false, typeSet, true);
  1496. }
  1497. void
  1498. GlobOpt::UpdateObjectTypeValue(Value* value, const JITTypeHolder type, bool setType, Js::EquivalentTypeSet* typeSet, bool setTypeSet)
  1499. {
  1500. Assert(value->GetValueInfo() != nullptr && value->GetValueInfo()->IsJsType());
  1501. JsTypeValueInfo* valueInfo = value->GetValueInfo()->AsJsType();
  1502. if (valueInfo->GetIsShared())
  1503. {
  1504. valueInfo = valueInfo->Copy(this->alloc);
  1505. value->SetValueInfo(valueInfo);
  1506. }
  1507. if (setType)
  1508. {
  1509. valueInfo->SetJsType(type);
  1510. }
  1511. if (setTypeSet)
  1512. {
  1513. valueInfo->SetJsTypeSet(typeSet);
  1514. }
  1515. }
  1516. void
  1517. GlobOpt::SetObjectTypeFromTypeSym(StackSym *typeSym, Value* value, BasicBlock* block)
  1518. {
  1519. Assert(typeSym != nullptr);
  1520. Assert(value != nullptr);
  1521. Assert(value->GetValueInfo() != nullptr && value->GetValueInfo()->IsJsType());
  1522. SymID typeSymId = typeSym->m_id;
  1523. if (block == nullptr)
  1524. {
  1525. block = this->currentBlock;
  1526. }
  1527. block->globOptData.SetValue(value, typeSym);
  1528. block->globOptData.liveFields->Set(typeSymId);
  1529. }
  1530. void
  1531. GlobOpt::SetObjectTypeFromTypeSym(StackSym *typeSym, const JITTypeHolder type, Js::EquivalentTypeSet * typeSet, BasicBlock* block, bool updateExistingValue)
  1532. {
  1533. if (block == nullptr)
  1534. {
  1535. block = this->currentBlock;
  1536. }
  1537. SetObjectTypeFromTypeSym(typeSym, type, typeSet, &block->globOptData, updateExistingValue);
  1538. }
  1539. void
  1540. GlobOpt::SetObjectTypeFromTypeSym(StackSym *typeSym, const JITTypeHolder type, Js::EquivalentTypeSet * typeSet, GlobOptBlockData *blockData, bool updateExistingValue)
  1541. {
  1542. Assert(typeSym != nullptr);
  1543. SymID typeSymId = typeSym->m_id;
  1544. if (blockData == nullptr)
  1545. {
  1546. blockData = &this->currentBlock->globOptData;
  1547. }
  1548. if (updateExistingValue)
  1549. {
  1550. Value* value = blockData->FindValueFromMapDirect(typeSymId);
  1551. // If we're trying to update an existing value, the value better exist. We only do this when updating a generic
  1552. // value created during loop pre-pass for field hoisting, so we expect the value info to still be blank.
  1553. Assert(value != nullptr && value->GetValueInfo() != nullptr && value->GetValueInfo()->IsJsType());
  1554. JsTypeValueInfo* valueInfo = value->GetValueInfo()->AsJsType();
  1555. Assert(valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr);
  1556. UpdateObjectTypeValue(value, type, true, typeSet, true);
  1557. }
  1558. else
  1559. {
  1560. JsTypeValueInfo* valueInfo = JsTypeValueInfo::New(this->alloc, type, typeSet);
  1561. this->SetSymStoreDirect(valueInfo, typeSym);
  1562. Value* value = NewValue(valueInfo);
  1563. blockData->SetValue(value, typeSym);
  1564. }
  1565. blockData->liveFields->Set(typeSymId);
  1566. }
  1567. void
  1568. GlobOpt::KillObjectType(StackSym* objectSym, BVSparse<JitArenaAllocator>* liveFields)
  1569. {
  1570. if (objectSym->IsTypeSpec())
  1571. {
  1572. objectSym = objectSym->GetVarEquivSym(this->func);
  1573. }
  1574. Assert(objectSym);
  1575. // We may be conservatively attempting to kill type syms from object syms that don't actually
  1576. // participate in object type specialization and hence don't actually have type syms (yet).
  1577. if (!objectSym->HasObjectTypeSym())
  1578. {
  1579. return;
  1580. }
  1581. if (liveFields == nullptr)
  1582. {
  1583. liveFields = this->currentBlock->globOptData.liveFields;
  1584. }
  1585. liveFields->Clear(objectSym->GetObjectTypeSym()->m_id);
  1586. StackSym *auxSlotPtrSym = objectSym->GetAuxSlotPtrSym();
  1587. if (auxSlotPtrSym)
  1588. {
  1589. liveFields->Clear(auxSlotPtrSym->m_id);
  1590. }
  1591. }
  1592. void
  1593. GlobOpt::KillAllObjectTypes(BVSparse<JitArenaAllocator>* liveFields)
  1594. {
  1595. if (this->objectTypeSyms && liveFields)
  1596. {
  1597. liveFields->Minus(this->objectTypeSyms);
  1598. liveFields->Minus(this->auxSlotPtrSyms);
  1599. }
  1600. }
  1601. void
  1602. GlobOpt::EndFieldLifetime(IR::SymOpnd *symOpnd)
  1603. {
  1604. this->currentBlock->globOptData.liveFields->Clear(symOpnd->m_sym->m_id);
  1605. }
  1606. PropertySym *
  1607. GlobOpt::CopyPropPropertySymObj(IR::SymOpnd *symOpnd, IR::Instr *instr)
  1608. {
  1609. Assert(symOpnd->m_sym->IsPropertySym());
  1610. PropertySym *propertySym = symOpnd->m_sym->AsPropertySym();
  1611. StackSym *objSym = propertySym->m_stackSym;
  1612. Value * val = this->currentBlock->globOptData.FindValue(objSym);
  1613. if (val && !PHASE_OFF(Js::ObjPtrCopyPropPhase, this->func))
  1614. {
  1615. StackSym *copySym = this->currentBlock->globOptData.GetCopyPropSym(objSym, val);
  1616. if (copySym != nullptr)
  1617. {
  1618. PropertySym *newProp = PropertySym::FindOrCreate(
  1619. copySym->m_id, propertySym->m_propertyId, propertySym->GetPropertyIdIndex(), propertySym->GetInlineCacheIndex(), propertySym->m_fieldKind, this->func);
  1620. if (!this->IsLoopPrePass() || SafeToCopyPropInPrepass(objSym, copySym, val))
  1621. {
  1622. #if DBG_DUMP
  1623. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::GlobOptPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  1624. {
  1625. Output::Print(_u("TRACE: "));
  1626. symOpnd->Dump();
  1627. Output::Print(_u(" : "));
  1628. Output::Print(_u("Copy prop obj ptr s%d, new property: "), copySym->m_id);
  1629. newProp->Dump();
  1630. Output::Print(_u("\n"));
  1631. }
  1632. #endif
  1633. // Copy prop
  1634. this->CaptureByteCodeSymUses(instr);
  1635. // If the old sym was part of an object type spec type check sequence,
  1636. // let's make sure the new one is prepped for it as well.
  1637. if (IsPropertySymPreparedForTypeCheckSeq(propertySym))
  1638. {
  1639. PreparePropertySymForTypeCheckSeq(newProp);
  1640. }
  1641. symOpnd->m_sym = newProp;
  1642. symOpnd->SetIsJITOptimizedReg(true);
  1643. if (symOpnd->IsPropertySymOpnd())
  1644. {
  1645. IR::PropertySymOpnd *propertySymOpnd = symOpnd->AsPropertySymOpnd();
  1646. if (propertySymOpnd->IsTypeCheckSeqCandidate())
  1647. {
  1648. // If the new pointer sym's expected type(s) don't match those in the inline-cache-based data for this access,
  1649. // we probably have a mismatch and can't safely objtypespec. If the saved objtypespecfldinfo isn't right for
  1650. // the new type, then we'll do an incorrect property access.
  1651. StackSym * newTypeSym = copySym->GetObjectTypeSym();
  1652. Value * newValue = currentBlock->globOptData.FindObjectTypeValueNoLivenessCheck(newTypeSym);
  1653. JsTypeValueInfo * newValueInfo = newValue ? newValue->GetValueInfo()->AsJsType() : nullptr;
  1654. bool shouldOptimize = CompareCurrentTypesWithExpectedTypes(newValueInfo, propertySymOpnd);
  1655. if (!shouldOptimize)
  1656. {
  1657. // We would like just to force a new type check here and keep optimizing, but downstream
  1658. // objtypespecfldinfo may have slot indices based on the old type.
  1659. propertySymOpnd->SetTypeCheckSeqCandidate(false);
  1660. }
  1661. }
  1662. // This is no longer strictly necessary, since we don't set the type dead bits in the initial
  1663. // backward pass, but let's keep it around for now in case we choose to revert to the old model.
  1664. propertySymOpnd->SetTypeDeadIfTypeCheckSeqCandidate(false);
  1665. }
  1666. if (this->IsLoopPrePass())
  1667. {
  1668. this->prePassCopyPropSym->Set(copySym->m_id);
  1669. }
  1670. }
  1671. propertySym = newProp;
  1672. if(instr->GetDst() && symOpnd->IsEqual(instr->GetDst()))
  1673. {
  1674. // Make sure any stack sym uses in the new destination property sym are unspecialized
  1675. instr = ToVarUses(instr, symOpnd, true, nullptr);
  1676. }
  1677. }
  1678. }
  1679. return propertySym;
  1680. }
  1681. void
  1682. GlobOpt::UpdateObjPtrValueType(IR::Opnd * opnd, IR::Instr * instr)
  1683. {
  1684. if (!opnd->IsSymOpnd() || !opnd->AsSymOpnd()->IsPropertySymOpnd())
  1685. {
  1686. return;
  1687. }
  1688. if (!instr->HasTypeCheckBailOut())
  1689. {
  1690. // No type check bailout, we didn't check that type of the object pointer.
  1691. return;
  1692. }
  1693. // Only check that fixed field should have type check bailout in loop prepass.
  1694. Assert(instr->m_opcode == Js::OpCode::CheckFixedFld || !this->IsLoopPrePass());
  1695. if (instr->m_opcode != Js::OpCode::CheckFixedFld)
  1696. {
  1697. // DeadStore pass may remove type check bailout, except CheckFixedFld which always needs
  1698. // type check bailout. So we can only change the type for CheckFixedFld.
  1699. // Consider: See if we can expand that in the future.
  1700. return;
  1701. }
  1702. IR::PropertySymOpnd * propertySymOpnd = opnd->AsPropertySymOpnd();
  1703. StackSym * objectSym = propertySymOpnd->GetObjectSym();
  1704. Value * objVal = this->currentBlock->globOptData.FindValue(objectSym);
  1705. if (!objVal)
  1706. {
  1707. return;
  1708. }
  1709. ValueType objValueType = objVal->GetValueInfo()->Type();
  1710. if (objValueType.IsDefinite())
  1711. {
  1712. return;
  1713. }
  1714. ValueInfo *objValueInfo = objVal->GetValueInfo();
  1715. // It is possible for a valueInfo to be not definite and still have a byteCodeConstant as symStore, this is because we conservatively copy valueInfo in prePass
  1716. if (objValueInfo->GetSymStore() && objValueInfo->GetSymStore()->IsStackSym() && objValueInfo->GetSymStore()->AsStackSym()->IsFromByteCodeConstantTable())
  1717. {
  1718. return;
  1719. }
  1720. // Verify that the types we're checking for here have been locked so that the type ID's can't be changed
  1721. // without changing the type.
  1722. if (!propertySymOpnd->HasObjectTypeSym())
  1723. {
  1724. return;
  1725. }
  1726. StackSym * typeSym = propertySymOpnd->GetObjectTypeSym();
  1727. Assert(typeSym);
  1728. Value * typeValue = currentBlock->globOptData.FindObjectTypeValue(typeSym);
  1729. if (!typeValue)
  1730. {
  1731. return;
  1732. }
  1733. JsTypeValueInfo * typeValueInfo = typeValue->GetValueInfo()->AsJsType();
  1734. JITTypeHolder type = typeValueInfo->GetJsType();
  1735. if (type != nullptr)
  1736. {
  1737. if (Js::DynamicType::Is(type->GetTypeId()) &&
  1738. !type->GetTypeHandler()->IsLocked())
  1739. {
  1740. return;
  1741. }
  1742. }
  1743. else
  1744. {
  1745. Js::EquivalentTypeSet * typeSet = typeValueInfo->GetJsTypeSet();
  1746. Assert(typeSet);
  1747. for (uint16 i = 0; i < typeSet->GetCount(); i++)
  1748. {
  1749. type = typeSet->GetType(i);
  1750. if (Js::DynamicType::Is(type->GetTypeId()) &&
  1751. !type->GetTypeHandler()->IsLocked())
  1752. {
  1753. return;
  1754. }
  1755. }
  1756. }
  1757. AnalysisAssert(type != nullptr);
  1758. Js::TypeId typeId = type->GetTypeId();
  1759. if (Js::TypedArrayBase::Is(typeId))
  1760. {
  1761. // Type ID does not allow us to distinguish between virtual and non-virtual typed array.
  1762. return;
  1763. }
  1764. // Passing false for useVirtual as we would never have a virtual typed array hitting this code path
  1765. ValueType newValueType = ValueType::FromTypeId(typeId, false);
  1766. if (newValueType == ValueType::Uninitialized)
  1767. {
  1768. switch (typeId)
  1769. {
  1770. default:
  1771. // Can't mark as definite object because it may actually be object-with-array.
  1772. // Consider: a value type that subsumes object, array, and object-with-array.
  1773. break;
  1774. case Js::TypeIds_NativeIntArray:
  1775. case Js::TypeIds_NativeFloatArray:
  1776. // Do not mark these values as definite to protect against array conversion
  1777. break;
  1778. case Js::TypeIds_Array:
  1779. // Because array can change type id, we can only make it definite if we are doing array check hoist
  1780. // so that implicit call will be installed between the array checks.
  1781. if (!DoArrayCheckHoist() ||
  1782. (currentBlock->loop
  1783. ? !this->ImplicitCallFlagsAllowOpts(currentBlock->loop)
  1784. : !this->ImplicitCallFlagsAllowOpts(this->func)))
  1785. {
  1786. break;
  1787. }
  1788. if (objValueType.IsLikelyArrayOrObjectWithArray())
  1789. {
  1790. // If we have likely no missing values before, keep the likely, because, we haven't proven that
  1791. // the array really has no missing values
  1792. if (!objValueType.HasNoMissingValues())
  1793. {
  1794. newValueType = ValueType::GetObject(ObjectType::Array).SetArrayTypeId(typeId);
  1795. }
  1796. }
  1797. else
  1798. {
  1799. newValueType = ValueType::GetObject(ObjectType::Array).SetArrayTypeId(typeId);
  1800. }
  1801. break;
  1802. }
  1803. }
  1804. if (newValueType != ValueType::Uninitialized)
  1805. {
  1806. ChangeValueType(currentBlock, objVal, newValueType, false, true);
  1807. }
  1808. }