GlobOptExpr.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  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. uint8 OpCodeToHash[Js::OpCode::Count];
  7. static Js::OpCode HashToOpCode[Js::OpCode::Count];
  8. class CSEInit
  9. {
  10. public:
  11. // Initializer for OpCodeToHash and HashToOpCode maps.
  12. CSEInit()
  13. {
  14. uint8 hash = 1;
  15. for (Js::OpCode opcode = (Js::OpCode)0; opcode < Js::OpCode::Count; opcode = (Js::OpCode)(opcode + 1))
  16. {
  17. if (Js::OpCodeUtil::IsValidOpcode(opcode) && OpCodeAttr::CanCSE(opcode) && !OpCodeAttr::ByteCodeOnly(opcode))
  18. {
  19. OpCodeToHash[(int)opcode] = hash;
  20. HashToOpCode[hash] = opcode;
  21. hash++;
  22. AssertMsg(hash != 0, "Too many CSE'able opcodes");
  23. }
  24. }
  25. }
  26. }CSEInit_Dummy;
  27. bool
  28. GlobOpt::GetHash(IR::Instr *instr, Value *src1Val, Value *src2Val, ExprAttributes exprAttributes, ExprHash *pHash)
  29. {
  30. Js::OpCode opcode = instr->m_opcode;
  31. // Candidate?
  32. if (!OpCodeAttr::CanCSE(opcode) && (opcode != Js::OpCode::StElemI_A && opcode != Js::OpCode::StElemI_A_Strict))
  33. {
  34. return false;
  35. }
  36. ValueNumber valNum1 = 0;
  37. ValueNumber valNum2 = 0;
  38. // Get the value number of src1 and src2
  39. if (instr->GetSrc1())
  40. {
  41. if (!src1Val)
  42. {
  43. return false;
  44. }
  45. valNum1 = src1Val->GetValueNumber();
  46. if (instr->GetSrc2())
  47. {
  48. if (!src2Val)
  49. {
  50. return false;
  51. }
  52. valNum2 = src2Val->GetValueNumber();
  53. }
  54. }
  55. if (src1Val)
  56. {
  57. valNum1 = src1Val->GetValueNumber();
  58. if (src2Val)
  59. {
  60. valNum2 = src2Val->GetValueNumber();
  61. }
  62. }
  63. switch (opcode)
  64. {
  65. case Js::OpCode::Ld_I4:
  66. case Js::OpCode::Ld_A:
  67. // Copy-prop should handle these
  68. return false;
  69. case Js::OpCode::Add_I4:
  70. case Js::OpCode::Add_Ptr:
  71. opcode = Js::OpCode::Add_A;
  72. break;
  73. case Js::OpCode::Sub_I4:
  74. opcode = Js::OpCode::Sub_A;
  75. break;
  76. case Js::OpCode::Mul_I4:
  77. opcode = Js::OpCode::Mul_A;
  78. break;
  79. case Js::OpCode::Rem_I4:
  80. opcode = Js::OpCode::Rem_A;
  81. break;
  82. case Js::OpCode::Div_I4:
  83. opcode = Js::OpCode::Div_A;
  84. break;
  85. case Js::OpCode::Neg_I4:
  86. opcode = Js::OpCode::Neg_A;
  87. break;
  88. case Js::OpCode::Not_I4:
  89. opcode = Js::OpCode::Not_A;
  90. break;
  91. case Js::OpCode::And_I4:
  92. opcode = Js::OpCode::And_A;
  93. break;
  94. case Js::OpCode::Or_I4:
  95. opcode = Js::OpCode::Or_A;
  96. break;
  97. case Js::OpCode::Xor_I4:
  98. opcode = Js::OpCode::Xor_A;
  99. break;
  100. case Js::OpCode::Shl_I4:
  101. opcode = Js::OpCode::Shl_A;
  102. break;
  103. case Js::OpCode::Shr_I4:
  104. opcode = Js::OpCode::Shr_A;
  105. break;
  106. case Js::OpCode::ShrU_I4:
  107. opcode = Js::OpCode::ShrU_A;
  108. break;
  109. case Js::OpCode::StElemI_A:
  110. case Js::OpCode::StElemI_A_Strict:
  111. // Make the load available as a CSE
  112. opcode = Js::OpCode::LdElemI_A;
  113. break;
  114. case Js::OpCode::CmEq_I4:
  115. opcode = Js::OpCode::CmEq_A;
  116. break;
  117. case Js::OpCode::CmNeq_I4:
  118. opcode = Js::OpCode::CmNeq_A;
  119. break;
  120. case Js::OpCode::CmLt_I4:
  121. opcode = Js::OpCode::CmLt_A;
  122. break;
  123. case Js::OpCode::CmLe_I4:
  124. opcode = Js::OpCode::CmLe_A;
  125. break;
  126. case Js::OpCode::CmGt_I4:
  127. opcode = Js::OpCode::CmGt_A;
  128. break;
  129. case Js::OpCode::CmGe_I4:
  130. opcode = Js::OpCode::CmGe_A;
  131. break;
  132. case Js::OpCode::CmUnLt_I4:
  133. opcode = Js::OpCode::CmUnLt_A;
  134. break;
  135. case Js::OpCode::CmUnLe_I4:
  136. opcode = Js::OpCode::CmUnLe_A;
  137. break;
  138. case Js::OpCode::CmUnGt_I4:
  139. opcode = Js::OpCode::CmUnGt_A;
  140. break;
  141. case Js::OpCode::CmUnGe_I4:
  142. opcode = Js::OpCode::CmUnGe_A;
  143. break;
  144. }
  145. pHash->Init(opcode, valNum1, valNum2, exprAttributes);
  146. #if DBG_DUMP
  147. if (!pHash->IsValid() && Js::Configuration::Global.flags.Trace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  148. {
  149. Output::Print(L" >>>> CSE: Value numbers too big to be hashed in function %s!\n", this->func->GetJnFunction()->GetDisplayName());
  150. }
  151. #endif
  152. #if ENABLE_DEBUG_CONFIG_OPTIONS
  153. if (!pHash->IsValid() && Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  154. {
  155. Output::Print(L" >>>> CSE: Value numbers too big to be hashed in function %s!\n", this->func->GetJnFunction()->GetDisplayName());
  156. }
  157. #endif
  158. return pHash->IsValid();
  159. }
  160. void
  161. GlobOpt::CSEAddInstr(
  162. BasicBlock *block,
  163. IR::Instr *instr,
  164. Value *dstVal,
  165. Value *src1Val,
  166. Value *src2Val,
  167. Value *dstIndirIndexVal,
  168. Value *src1IndirIndexVal)
  169. {
  170. ExprAttributes exprAttributes;
  171. ExprHash hash;
  172. if (!this->DoCSE())
  173. {
  174. return;
  175. }
  176. bool isArray = false;
  177. switch(instr->m_opcode)
  178. {
  179. case Js::OpCode::LdElemI_A:
  180. case Js::OpCode::LdInt8ArrViewElem:
  181. case Js::OpCode::LdUInt8ArrViewElem:
  182. case Js::OpCode::LdInt16ArrViewElem:
  183. case Js::OpCode::LdUInt16ArrViewElem:
  184. case Js::OpCode::LdInt32ArrViewElem:
  185. case Js::OpCode::LdUInt32ArrViewElem:
  186. case Js::OpCode::LdFloat32ArrViewElem:
  187. case Js::OpCode::LdFloat64ArrViewElem:
  188. case Js::OpCode::StElemI_A:
  189. case Js::OpCode::StElemI_A_Strict:
  190. {
  191. // For arrays, hash the value # of the baseOpnd and indexOpnd
  192. IR::IndirOpnd *arrayOpnd;
  193. Value *indirIndexVal;
  194. if (instr->m_opcode == Js::OpCode::StElemI_A || instr->m_opcode == Js::OpCode::StElemI_A_Strict)
  195. {
  196. if (!this->CanCSEArrayStore(instr))
  197. {
  198. return;
  199. }
  200. dstVal = src1Val;
  201. arrayOpnd = instr->GetDst()->AsIndirOpnd();
  202. indirIndexVal = dstIndirIndexVal;
  203. }
  204. else
  205. {
  206. // all the LdElem and Ld*ArrViewElem
  207. arrayOpnd = instr->GetSrc1()->AsIndirOpnd();
  208. indirIndexVal = src1IndirIndexVal;
  209. }
  210. src1Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetBaseOpnd()->m_sym);
  211. if(indirIndexVal)
  212. {
  213. src2Val = indirIndexVal;
  214. }
  215. else if (arrayOpnd->GetIndexOpnd())
  216. {
  217. src2Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetIndexOpnd()->m_sym);
  218. }
  219. else
  220. {
  221. return;
  222. }
  223. isArray = true;
  224. // for typed array do not add instructions whose dst are guaranteed to be int or number
  225. // as we will try to eliminate bound check for these typed arrays
  226. if (src1Val->GetValueInfo()->IsLikelyOptimizedVirtualTypedArray())
  227. {
  228. exprAttributes = DstIsIntOrNumberAttributes(!instr->dstIsAlwaysConvertedToInt32, !instr->dstIsAlwaysConvertedToNumber);
  229. }
  230. break;
  231. }
  232. case Js::OpCode::Mul_I4:
  233. // If int32 overflow is ignored, we only add MULs with 53-bit overflow check to expr map
  234. if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & IR::BailOutOnMulOverflow) &&
  235. !instr->ShouldCheckFor32BitOverflow() && instr->ignoreOverflowBitCount != 53)
  236. {
  237. return;
  238. }
  239. // fall-through
  240. case Js::OpCode::Neg_I4:
  241. case Js::OpCode::Add_I4:
  242. case Js::OpCode::Sub_I4:
  243. case Js::OpCode::Div_I4:
  244. case Js::OpCode::Rem_I4:
  245. case Js::OpCode::Add_Ptr:
  246. case Js::OpCode::ShrU_I4:
  247. {
  248. // Can't CSE and Add where overflow doesn't matter (and no bailout) with one where it does matter... Record whether int
  249. // overflow or negative zero were ignored.
  250. exprAttributes = IntMathExprAttributes(ignoredIntOverflowForCurrentInstr, ignoredNegativeZeroForCurrentInstr);
  251. break;
  252. }
  253. case Js::OpCode::InlineMathFloor:
  254. case Js::OpCode::InlineMathCeil:
  255. case Js::OpCode::InlineMathRound:
  256. if (!instr->ShouldCheckForNegativeZero())
  257. {
  258. return;
  259. }
  260. break;
  261. }
  262. ValueInfo *valueInfo = NULL;
  263. if (instr->GetDst())
  264. {
  265. if (!dstVal)
  266. {
  267. return;
  268. }
  269. valueInfo = dstVal->GetValueInfo();
  270. if(valueInfo->GetSymStore() == NULL &&
  271. !(isArray && valueInfo->HasIntConstantValue() && valueInfo->IsIntAndLikelyTagged() && instr->GetSrc1()->IsAddrOpnd()))
  272. {
  273. return;
  274. }
  275. }
  276. if (!this->GetHash(instr, src1Val, src2Val, exprAttributes, &hash))
  277. {
  278. return;
  279. }
  280. int32 intConstantValue;
  281. if(valueInfo && !valueInfo->GetSymStore() && valueInfo->TryGetIntConstantValue(&intConstantValue))
  282. {
  283. Assert(isArray);
  284. Assert(valueInfo->IsIntAndLikelyTagged());
  285. Assert(instr->GetSrc1()->IsAddrOpnd());
  286. // We need a sym associated with a value in the expression value table. Hoist the address into a stack sym associated
  287. // with the int constant value.
  288. StackSym *const constStackSym = GetOrCreateTaggedIntConstantStackSym(intConstantValue);
  289. instr->HoistSrc1(Js::OpCode::Ld_A, RegNOREG, constStackSym);
  290. SetValue(&blockData, dstVal, constStackSym);
  291. }
  292. // We have a candidate. Add it to the exprToValueMap.
  293. Value ** pVal = block->globOptData.exprToValueMap->FindOrInsertNew(hash);
  294. *pVal = dstVal;
  295. if (isArray)
  296. {
  297. block->globOptData.liveArrayValues->Set(hash);
  298. }
  299. if (MayNeedBailOnImplicitCall(instr, src1Val, src2Val))
  300. {
  301. this->currentBlock->globOptData.hasCSECandidates = true;
  302. // Use LiveFields to track is object.valueOf/toString could get overridden.
  303. IR::Opnd *src1 = instr->GetSrc1();
  304. if (src1)
  305. {
  306. if (src1->IsRegOpnd())
  307. {
  308. StackSym *varSym = src1->AsRegOpnd()->m_sym;
  309. if (varSym->IsTypeSpec())
  310. {
  311. varSym = varSym->GetVarEquivSym(this->func);
  312. }
  313. block->globOptData.liveFields->Set(varSym->m_id);
  314. }
  315. IR::Opnd *src2 = instr->GetSrc2();
  316. if (src2 && src2->IsRegOpnd())
  317. {
  318. StackSym *varSym = src2->AsRegOpnd()->m_sym;
  319. if (varSym->IsTypeSpec())
  320. {
  321. varSym = varSym->GetVarEquivSym(this->func);
  322. }
  323. block->globOptData.liveFields->Set(varSym->m_id);
  324. }
  325. }
  326. }
  327. }
  328. bool
  329. GlobOpt::CSEOptimize(BasicBlock *block, IR::Instr * *const instrRef, Value **pSrc1Val, Value **pSrc2Val, Value **pSrc1IndirIndexVal, bool intMathExprOnly)
  330. {
  331. Assert(instrRef);
  332. IR::Instr *&instr = *instrRef;
  333. Assert(instr);
  334. if (!this->DoCSE())
  335. {
  336. return false;
  337. }
  338. Value *src1Val = *pSrc1Val;
  339. Value *src2Val = *pSrc2Val;
  340. Value *src1IndirIndexVal = *pSrc1IndirIndexVal;
  341. bool isArray = false;
  342. ExprAttributes exprAttributes;
  343. ExprHash hash;
  344. // For arrays, hash the value # of the baseOpnd and indexOpnd
  345. switch(instr->m_opcode)
  346. {
  347. case Js::OpCode::LdInt8ArrViewElem:
  348. case Js::OpCode::LdUInt8ArrViewElem:
  349. case Js::OpCode::LdInt16ArrViewElem:
  350. case Js::OpCode::LdUInt16ArrViewElem:
  351. case Js::OpCode::LdInt32ArrViewElem:
  352. case Js::OpCode::LdUInt32ArrViewElem:
  353. case Js::OpCode::LdFloat32ArrViewElem:
  354. case Js::OpCode::LdFloat64ArrViewElem:
  355. case Js::OpCode::LdElemI_A:
  356. {
  357. if(intMathExprOnly)
  358. {
  359. return false;
  360. }
  361. IR::IndirOpnd *arrayOpnd = instr->GetSrc1()->AsIndirOpnd();
  362. src1Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetBaseOpnd()->m_sym);
  363. if(src1IndirIndexVal)
  364. {
  365. src2Val = src1IndirIndexVal;
  366. }
  367. else if (arrayOpnd->GetIndexOpnd())
  368. {
  369. src2Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetIndexOpnd()->m_sym);
  370. }
  371. else
  372. {
  373. return false;
  374. }
  375. // for typed array do not add instructions whose dst are guaranteed to be int or number
  376. // as we will try to eliminate bound check for these typed arrays
  377. if (src1Val->GetValueInfo()->IsLikelyOptimizedVirtualTypedArray())
  378. {
  379. exprAttributes = DstIsIntOrNumberAttributes(!instr->dstIsAlwaysConvertedToInt32, !instr->dstIsAlwaysConvertedToNumber);
  380. }
  381. isArray = true;
  382. break;
  383. }
  384. case Js::OpCode::Neg_A:
  385. case Js::OpCode::Add_A:
  386. case Js::OpCode::Sub_A:
  387. case Js::OpCode::Mul_A:
  388. case Js::OpCode::Div_A:
  389. case Js::OpCode::Rem_A:
  390. case Js::OpCode::ShrU_A:
  391. // If the previously-computed matching expression ignored int overflow or negative zero, those attributes must match
  392. // to be able to CSE this expression
  393. if(intMathExprOnly && !ignoredIntOverflowForCurrentInstr && !ignoredNegativeZeroForCurrentInstr)
  394. {
  395. // Already tried CSE with default attributes
  396. return false;
  397. }
  398. exprAttributes = IntMathExprAttributes(ignoredIntOverflowForCurrentInstr, ignoredNegativeZeroForCurrentInstr);
  399. break;
  400. default:
  401. if(intMathExprOnly)
  402. {
  403. return false;
  404. }
  405. break;
  406. }
  407. if (!this->GetHash(instr, src1Val, src2Val, exprAttributes, &hash))
  408. {
  409. return false;
  410. }
  411. // See if we have a value for that expression
  412. Value ** pVal = block->globOptData.exprToValueMap->Get(hash);
  413. if (pVal == NULL)
  414. {
  415. return false;
  416. }
  417. ValueInfo *valueInfo = NULL;
  418. Sym *symStore = NULL;
  419. Value *val = NULL;
  420. if (instr->GetDst())
  421. {
  422. if (*pVal == NULL)
  423. {
  424. return false;
  425. }
  426. val = *pVal;
  427. // Make sure the array value is still live. We can't CSE something like:
  428. // ... = A[i];
  429. // B[j] = ...;
  430. // ... = A[i];
  431. if (isArray && !block->globOptData.liveArrayValues->Test(hash))
  432. {
  433. return false;
  434. }
  435. // See if the symStore is still valid
  436. valueInfo = val->GetValueInfo();
  437. symStore = valueInfo->GetSymStore();
  438. Value * symStoreVal = NULL;
  439. int32 intConstantValue;
  440. if (!symStore && valueInfo->TryGetIntConstantValue(&intConstantValue))
  441. {
  442. // Handle:
  443. // A[i] = 10;
  444. // ... = A[i];
  445. if (!isArray)
  446. {
  447. return false;
  448. }
  449. if (!valueInfo->IsIntAndLikelyTagged())
  450. {
  451. return false;
  452. }
  453. symStore = GetTaggedIntConstantStackSym(intConstantValue);
  454. }
  455. if(!symStore || !symStore->IsStackSym())
  456. {
  457. return false;
  458. }
  459. symStoreVal = this->FindValue(block->globOptData.symToValueMap, symStore);
  460. if (!symStoreVal || symStoreVal->GetValueNumber() != val->GetValueNumber())
  461. {
  462. return false;
  463. }
  464. val = symStoreVal;
  465. valueInfo = val->GetValueInfo();
  466. }
  467. // Make sure srcs still have same values
  468. if (instr->GetSrc1())
  469. {
  470. if (!src1Val)
  471. {
  472. return false;
  473. }
  474. if (hash.GetSrc1ValueNumber() != src1Val->GetValueNumber())
  475. {
  476. return false;
  477. }
  478. IR::Opnd *src1 = instr->GetSrc1();
  479. if (src1->IsSymOpnd() && src1->AsSymOpnd()->IsPropertySymOpnd())
  480. {
  481. Assert(instr->m_opcode == Js::OpCode::CheckFixedFld);
  482. IR::PropertySymOpnd *propOpnd = src1->AsSymOpnd()->AsPropertySymOpnd();
  483. if (!propOpnd->IsTypeChecked() && !propOpnd->IsRootObjectNonConfigurableFieldLoad())
  484. {
  485. // Require m_CachedTypeChecked for 2 reasons:
  486. // - We may be relying on this instruction to do a type check for a downstream reference.
  487. // - This instruction may have a different inline cache, and thus need to check a different type,
  488. // than the upstream check.
  489. // REVIEW: We could process this differently somehow to get the type check on the next reference.
  490. return false;
  491. }
  492. }
  493. if (instr->GetSrc2())
  494. {
  495. if (!src2Val)
  496. {
  497. return false;
  498. }
  499. if (hash.GetSrc2ValueNumber() != src2Val->GetValueNumber())
  500. {
  501. return false;
  502. }
  503. }
  504. }
  505. bool needsBailOnImplicitCall = false;
  506. // Need implicit call bailouts?
  507. if (this->MayNeedBailOnImplicitCall(instr, src1Val, src2Val))
  508. {
  509. needsBailOnImplicitCall = true;
  510. IR::Opnd *src1 = instr->GetSrc1();
  511. if (instr->m_opcode != Js::OpCode::StElemI_A && instr->m_opcode != Js::OpCode::StElemI_A_Strict
  512. && src1 && src1->IsRegOpnd())
  513. {
  514. StackSym *sym = src1->AsRegOpnd()->m_sym;
  515. if (this->IsTypeSpecialized(sym, block) || block->globOptData.liveInt32Syms->Test(sym->m_id))
  516. {
  517. IR::Opnd *src2 = instr->GetSrc2();
  518. if (!src2 || src2->IsImmediateOpnd())
  519. {
  520. needsBailOnImplicitCall = false;
  521. }
  522. else if (src2->IsRegOpnd())
  523. {
  524. StackSym *sym = src2->AsRegOpnd()->m_sym;
  525. if (this->IsTypeSpecialized(src2->AsRegOpnd()->m_sym, block) || block->globOptData.liveInt32Syms->Test(sym->m_id))
  526. {
  527. needsBailOnImplicitCall = false;
  528. }
  529. }
  530. }
  531. }
  532. }
  533. if (needsBailOnImplicitCall)
  534. {
  535. IR::Opnd *src1 = instr->GetSrc1();
  536. if (src1)
  537. {
  538. if (src1->IsRegOpnd())
  539. {
  540. StackSym *varSym = src1->AsRegOpnd()->m_sym;
  541. Assert(!varSym->IsTypeSpec());
  542. if (!block->globOptData.liveFields->Test(varSym->m_id))
  543. {
  544. return false;
  545. }
  546. IR::Opnd *src2 = instr->GetSrc2();
  547. if (src2 && src2->IsRegOpnd())
  548. {
  549. StackSym *varSym = src2->AsRegOpnd()->m_sym;
  550. Assert(!varSym->IsTypeSpec());
  551. if (!block->globOptData.liveFields->Test(varSym->m_id))
  552. {
  553. return false;
  554. }
  555. }
  556. }
  557. }
  558. }
  559. // in asmjs we can have a symstore with a different type
  560. // x = HEAPF32[i >> 2]
  561. // y = HEAPI32[i >> 2]
  562. if (instr->GetDst() && (instr->GetDst()->GetType() != symStore->AsStackSym()->GetType()))
  563. {
  564. Assert(GetIsAsmJSFunc());
  565. return false;
  566. }
  567. // SIMD_JS
  568. if (instr->m_opcode == Js::OpCode::ExtendArg_A)
  569. {
  570. // we don't want to CSE ExtendArgs, only the operation using them. To do that, we mimic CSE by transferring the symStore valueInfo to the dst.
  571. IR::Opnd *dst = instr->GetDst();
  572. Value *dstVal = this->FindValue(symStore);
  573. this->SetValue(&this->blockData, dstVal, dst);
  574. dst->AsRegOpnd()->m_sym->CopySymAttrs(symStore->AsStackSym());
  575. return false;
  576. }
  577. //
  578. // Success, do the CSE rewrite.
  579. //
  580. #if DBG_DUMP
  581. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  582. {
  583. Output::Print(L" --- CSE (%s): ", this->func->GetJnFunction()->GetDisplayName());
  584. instr->Dump();
  585. }
  586. #endif
  587. #if ENABLE_DEBUG_CONFIG_OPTIONS
  588. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  589. {
  590. Output::Print(L" --- CSE (%s): %s\n", this->func->GetJnFunction()->GetDisplayName(), Js::OpCodeUtil::GetOpCodeName(instr->m_opcode));
  591. }
  592. #endif
  593. this->CaptureByteCodeSymUses(instr);
  594. if (!instr->GetDst())
  595. {
  596. instr->m_opcode = Js::OpCode::Nop;
  597. return true;
  598. }
  599. AnalysisAssert(valueInfo);
  600. IR::Opnd *cseOpnd;
  601. cseOpnd = IR::RegOpnd::New(symStore->AsStackSym(), instr->GetDst()->GetType(), instr->m_func);
  602. cseOpnd->SetValueType(valueInfo->Type());
  603. cseOpnd->SetIsJITOptimizedReg(true);
  604. if (needsBailOnImplicitCall)
  605. {
  606. this->CaptureNoImplicitCallUses(cseOpnd, false);
  607. }
  608. int32 intConstantValue;
  609. if (valueInfo->TryGetIntConstantValue(&intConstantValue) && valueInfo->IsIntAndLikelyTagged())
  610. {
  611. cseOpnd->Free(func);
  612. cseOpnd = IR::AddrOpnd::New(Js::TaggedInt::ToVarUnchecked(intConstantValue), IR::AddrOpndKindConstantVar, instr->m_func);
  613. cseOpnd->SetValueType(valueInfo->Type());
  614. }
  615. *pSrc1Val = val;
  616. {
  617. // Profiled instructions have data that is interpreted differently based on the op code. Since we're changing the op
  618. // code and due to other similar potential issues, always create a new instr instead of changing the existing one.
  619. IR::Instr *const originalInstr = instr;
  620. instr = IR::Instr::New(Js::OpCode::Ld_A, instr->GetDst(), cseOpnd, instr->m_func);
  621. originalInstr->TransferDstAttributesTo(instr);
  622. block->InsertInstrBefore(instr, originalInstr);
  623. block->RemoveInstr(originalInstr);
  624. }
  625. *pSrc2Val = NULL;
  626. *pSrc1IndirIndexVal = NULL;
  627. return true;
  628. }
  629. void
  630. GlobOpt::ProcessArrayValueKills(IR::Instr *instr)
  631. {
  632. switch (instr->m_opcode)
  633. {
  634. case Js::OpCode::StElemI_A:
  635. case Js::OpCode::StElemI_A_Strict:
  636. case Js::OpCode::DeleteElemI_A:
  637. case Js::OpCode::DeleteElemIStrict_A:
  638. case Js::OpCode::StFld:
  639. case Js::OpCode::StRootFld:
  640. case Js::OpCode::StFldStrict:
  641. case Js::OpCode::StRootFldStrict:
  642. case Js::OpCode::StSlot:
  643. case Js::OpCode::StSlotChkUndecl:
  644. case Js::OpCode::DeleteFld:
  645. case Js::OpCode::DeleteRootFld:
  646. case Js::OpCode::DeleteFldStrict:
  647. case Js::OpCode::DeleteRootFldStrict:
  648. case Js::OpCode::StInt8ArrViewElem:
  649. case Js::OpCode::StUInt8ArrViewElem:
  650. case Js::OpCode::StInt16ArrViewElem:
  651. case Js::OpCode::StUInt16ArrViewElem:
  652. case Js::OpCode::StInt32ArrViewElem:
  653. case Js::OpCode::StUInt32ArrViewElem:
  654. case Js::OpCode::StFloat32ArrViewElem:
  655. case Js::OpCode::StFloat64ArrViewElem:
  656. // These array helpers may change A.length (and A[i] could be A.length)...
  657. case Js::OpCode::InlineArrayPush:
  658. case Js::OpCode::InlineArrayPop:
  659. this->blockData.liveArrayValues->ClearAll();
  660. break;
  661. case Js::OpCode::CallDirect:
  662. Assert(instr->GetSrc1());
  663. switch(instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper)
  664. {
  665. // These array helpers may change A[i]
  666. case IR::HelperArray_Reverse:
  667. case IR::HelperArray_Shift:
  668. case IR::HelperArray_Unshift:
  669. case IR::HelperArray_Splice:
  670. this->blockData.liveArrayValues->ClearAll();
  671. break;
  672. }
  673. break;
  674. default:
  675. if (instr->UsesAllFields())
  676. {
  677. this->blockData.liveArrayValues->ClearAll();
  678. }
  679. break;
  680. }
  681. }
  682. bool
  683. GlobOpt::NeedBailOnImplicitCallForCSE(BasicBlock *block, bool isForwardPass)
  684. {
  685. return isForwardPass && block->globOptData.hasCSECandidates;
  686. }
  687. bool
  688. GlobOpt::DoCSE()
  689. {
  690. if (PHASE_OFF(Js::CSEPhase, this->func))
  691. {
  692. return false;
  693. }
  694. if (this->IsLoopPrePass())
  695. {
  696. return false;
  697. }
  698. if (PHASE_FORCE(Js::CSEPhase, this->func))
  699. {
  700. // Force always turn it on
  701. return true;
  702. }
  703. if (!this->DoFieldOpts(this->currentBlock->loop) && !GetIsAsmJSFunc())
  704. {
  705. return false;
  706. }
  707. return true;
  708. }
  709. bool
  710. GlobOpt::CanCSEArrayStore(IR::Instr *instr)
  711. {
  712. IR::Opnd *arrayOpnd = instr->GetDst();
  713. Assert(arrayOpnd->IsIndirOpnd());
  714. IR::RegOpnd *baseOpnd = arrayOpnd->AsIndirOpnd()->GetBaseOpnd();
  715. ValueType baseValueType(baseOpnd->GetValueType());
  716. // Only handle definite arrays for now. Typed Arrays would require truncation of the CSE'd value.
  717. if (!baseValueType.IsArrayOrObjectWithArray())
  718. {
  719. return false;
  720. }
  721. return true;
  722. }
  723. #if DBG_DUMP
  724. void
  725. DumpExpr(ExprHash hash)
  726. {
  727. Output::Print(L"Opcode: %10s src1Val: %d src2Val: %d\n", Js::OpCodeUtil::GetOpCodeName(HashToOpCode[(int)hash.GetOpcode()]), hash.GetSrc1ValueNumber(), hash.GetSrc2ValueNumber());
  728. }
  729. #endif