GlobOptExpr.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. uint8 OpCodeToHash[(uint)Js::OpCode::Count];
  7. static Js::OpCode HashToOpCode[(uint)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(_u(" >>>> CSE: Value numbers too big to be hashed in function %s!\n"), this->func->GetJITFunctionBody()->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(_u(" >>>> CSE: Value numbers too big to be hashed in function %s!\n"), this->func->GetJITFunctionBody()->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::LdArrViewElem:
  181. case Js::OpCode::StElemI_A:
  182. case Js::OpCode::StElemI_A_Strict:
  183. {
  184. // For arrays, hash the value # of the baseOpnd and indexOpnd
  185. IR::IndirOpnd *arrayOpnd;
  186. Value *indirIndexVal;
  187. if (instr->m_opcode == Js::OpCode::StElemI_A || instr->m_opcode == Js::OpCode::StElemI_A_Strict)
  188. {
  189. if (!this->CanCSEArrayStore(instr))
  190. {
  191. return;
  192. }
  193. dstVal = src1Val;
  194. arrayOpnd = instr->GetDst()->AsIndirOpnd();
  195. indirIndexVal = dstIndirIndexVal;
  196. }
  197. else
  198. {
  199. // all the LdElem and Ld*ArrViewElem
  200. arrayOpnd = instr->GetSrc1()->AsIndirOpnd();
  201. indirIndexVal = src1IndirIndexVal;
  202. }
  203. src1Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetBaseOpnd()->m_sym);
  204. if(indirIndexVal)
  205. {
  206. src2Val = indirIndexVal;
  207. }
  208. else if (arrayOpnd->GetIndexOpnd())
  209. {
  210. src2Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetIndexOpnd()->m_sym);
  211. }
  212. else
  213. {
  214. return;
  215. }
  216. isArray = true;
  217. // for typed array do not add instructions whose dst are guaranteed to be int or number
  218. // as we will try to eliminate bound check for these typed arrays
  219. if (src1Val->GetValueInfo()->IsLikelyOptimizedVirtualTypedArray())
  220. {
  221. exprAttributes = DstIsIntOrNumberAttributes(!instr->dstIsAlwaysConvertedToInt32, !instr->dstIsAlwaysConvertedToNumber);
  222. }
  223. break;
  224. }
  225. case Js::OpCode::Mul_I4:
  226. // If int32 overflow is ignored, we only add MULs with 53-bit overflow check to expr map
  227. if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & IR::BailOutOnMulOverflow) &&
  228. !instr->ShouldCheckFor32BitOverflow() && instr->ignoreOverflowBitCount != 53)
  229. {
  230. return;
  231. }
  232. // fall-through
  233. case Js::OpCode::Neg_I4:
  234. case Js::OpCode::Add_I4:
  235. case Js::OpCode::Sub_I4:
  236. case Js::OpCode::Div_I4:
  237. case Js::OpCode::Rem_I4:
  238. case Js::OpCode::Add_Ptr:
  239. case Js::OpCode::ShrU_I4:
  240. {
  241. // Can't CSE and Add where overflow doesn't matter (and no bailout) with one where it does matter... Record whether int
  242. // overflow or negative zero were ignored.
  243. exprAttributes = IntMathExprAttributes(ignoredIntOverflowForCurrentInstr, ignoredNegativeZeroForCurrentInstr);
  244. break;
  245. }
  246. case Js::OpCode::InlineMathFloor:
  247. case Js::OpCode::InlineMathCeil:
  248. case Js::OpCode::InlineMathRound:
  249. if (!instr->ShouldCheckForNegativeZero())
  250. {
  251. return;
  252. }
  253. break;
  254. }
  255. ValueInfo *valueInfo = NULL;
  256. if (instr->GetDst())
  257. {
  258. if (!dstVal)
  259. {
  260. return;
  261. }
  262. valueInfo = dstVal->GetValueInfo();
  263. if(valueInfo->GetSymStore() == NULL &&
  264. !(isArray && valueInfo->HasIntConstantValue() && valueInfo->IsIntAndLikelyTagged() && instr->GetSrc1()->IsAddrOpnd()))
  265. {
  266. return;
  267. }
  268. }
  269. if (!this->GetHash(instr, src1Val, src2Val, exprAttributes, &hash))
  270. {
  271. return;
  272. }
  273. int32 intConstantValue;
  274. if(valueInfo && !valueInfo->GetSymStore() && valueInfo->TryGetIntConstantValue(&intConstantValue))
  275. {
  276. Assert(isArray);
  277. Assert(valueInfo->IsIntAndLikelyTagged());
  278. Assert(instr->GetSrc1()->IsAddrOpnd());
  279. // We need a sym associated with a value in the expression value table. Hoist the address into a stack sym associated
  280. // with the int constant value.
  281. StackSym *const constStackSym = GetOrCreateTaggedIntConstantStackSym(intConstantValue);
  282. instr->HoistSrc1(Js::OpCode::Ld_A, RegNOREG, constStackSym);
  283. SetValue(&blockData, dstVal, constStackSym);
  284. }
  285. // We have a candidate. Add it to the exprToValueMap.
  286. Value ** pVal = block->globOptData.exprToValueMap->FindOrInsertNew(hash);
  287. *pVal = dstVal;
  288. if (isArray)
  289. {
  290. block->globOptData.liveArrayValues->Set(hash);
  291. }
  292. if (MayNeedBailOnImplicitCall(instr, src1Val, src2Val))
  293. {
  294. this->currentBlock->globOptData.hasCSECandidates = true;
  295. // Use LiveFields to track is object.valueOf/toString could get overridden.
  296. IR::Opnd *src1 = instr->GetSrc1();
  297. if (src1)
  298. {
  299. if (src1->IsRegOpnd())
  300. {
  301. StackSym *varSym = src1->AsRegOpnd()->m_sym;
  302. if (varSym->IsTypeSpec())
  303. {
  304. varSym = varSym->GetVarEquivSym(this->func);
  305. }
  306. block->globOptData.liveFields->Set(varSym->m_id);
  307. }
  308. IR::Opnd *src2 = instr->GetSrc2();
  309. if (src2 && src2->IsRegOpnd())
  310. {
  311. StackSym *varSym = src2->AsRegOpnd()->m_sym;
  312. if (varSym->IsTypeSpec())
  313. {
  314. varSym = varSym->GetVarEquivSym(this->func);
  315. }
  316. block->globOptData.liveFields->Set(varSym->m_id);
  317. }
  318. }
  319. }
  320. }
  321. static void TransformIntoUnreachable(IntConstType errorCode, IR::Instr* instr)
  322. {
  323. instr->m_opcode = Js::OpCode::Unreachable_Void;
  324. instr->ReplaceSrc1(IR::IntConstOpnd::New(SCODE_CODE(errorCode), TyInt32, instr->m_func));
  325. instr->UnlinkDst();
  326. }
  327. void
  328. GlobOpt::OptimizeChecks(IR::Instr * const instr, Value *src1Val, Value *src2Val)
  329. {
  330. int val = 0;
  331. switch (instr->m_opcode)
  332. {
  333. case Js::OpCode::TrapIfZero:
  334. if (instr->GetDst()->IsInt64())
  335. {
  336. return; //don't try to optimize i64 division since we are using helpers anyways for now
  337. }
  338. if (src1Val && src1Val->GetValueInfo()->TryGetIntConstantValue(&val))
  339. {
  340. if (val)
  341. {
  342. instr->m_opcode = Js::OpCode::Ld_I4;
  343. }
  344. else
  345. {
  346. TransformIntoUnreachable(WASMERR_DivideByZero, instr);
  347. InsertByteCodeUses(instr);
  348. RemoveCodeAfterNoFallthroughInstr(instr); //remove dead code
  349. }
  350. }
  351. break;
  352. case Js::OpCode::TrapIfMinIntOverNegOne:
  353. {
  354. if (instr->GetDst()->IsInt64())
  355. {
  356. return; //don't try to optimize i64 division since we are using helpers anyways for now
  357. }
  358. int checksLeft = 2;
  359. if (src1Val && src1Val->GetValueInfo()->TryGetIntConstantValue(&val))
  360. {
  361. if (val != INT_MIN)
  362. {
  363. instr->m_opcode = Js::OpCode::Ld_I4;
  364. }
  365. else
  366. {
  367. checksLeft--;
  368. }
  369. }
  370. if (src2Val && src2Val->GetValueInfo()->TryGetIntConstantValue(&val))
  371. {
  372. if (val != -1)
  373. {
  374. instr->m_opcode = Js::OpCode::Ld_I4;
  375. }
  376. else
  377. {
  378. checksLeft--;
  379. }
  380. }
  381. if (!checksLeft)
  382. {
  383. TransformIntoUnreachable(VBSERR_Overflow, instr);
  384. instr->UnlinkSrc2();
  385. InsertByteCodeUses(instr);
  386. RemoveCodeAfterNoFallthroughInstr(instr); //remove dead code
  387. }
  388. break;
  389. }
  390. default:
  391. return;
  392. }
  393. }
  394. bool
  395. GlobOpt::CSEOptimize(BasicBlock *block, IR::Instr * *const instrRef, Value **pSrc1Val, Value **pSrc2Val, Value **pSrc1IndirIndexVal, bool intMathExprOnly)
  396. {
  397. Assert(instrRef);
  398. IR::Instr *&instr = *instrRef;
  399. Assert(instr);
  400. if (!this->DoCSE())
  401. {
  402. return false;
  403. }
  404. Value *src1Val = *pSrc1Val;
  405. Value *src2Val = *pSrc2Val;
  406. Value *src1IndirIndexVal = *pSrc1IndirIndexVal;
  407. bool isArray = false;
  408. ExprAttributes exprAttributes;
  409. ExprHash hash;
  410. // For arrays, hash the value # of the baseOpnd and indexOpnd
  411. switch(instr->m_opcode)
  412. {
  413. case Js::OpCode::LdArrViewElem:
  414. case Js::OpCode::LdElemI_A:
  415. {
  416. if(intMathExprOnly)
  417. {
  418. return false;
  419. }
  420. IR::IndirOpnd *arrayOpnd = instr->GetSrc1()->AsIndirOpnd();
  421. src1Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetBaseOpnd()->m_sym);
  422. if(src1IndirIndexVal)
  423. {
  424. src2Val = src1IndirIndexVal;
  425. }
  426. else if (arrayOpnd->GetIndexOpnd())
  427. {
  428. src2Val = this->FindValue(block->globOptData.symToValueMap, arrayOpnd->GetIndexOpnd()->m_sym);
  429. }
  430. else
  431. {
  432. return false;
  433. }
  434. // for typed array do not add instructions whose dst are guaranteed to be int or number
  435. // as we will try to eliminate bound check for these typed arrays
  436. if (src1Val->GetValueInfo()->IsLikelyOptimizedVirtualTypedArray())
  437. {
  438. exprAttributes = DstIsIntOrNumberAttributes(!instr->dstIsAlwaysConvertedToInt32, !instr->dstIsAlwaysConvertedToNumber);
  439. }
  440. isArray = true;
  441. break;
  442. }
  443. case Js::OpCode::Neg_A:
  444. case Js::OpCode::Add_A:
  445. case Js::OpCode::Sub_A:
  446. case Js::OpCode::Mul_A:
  447. case Js::OpCode::Div_A:
  448. case Js::OpCode::Rem_A:
  449. case Js::OpCode::ShrU_A:
  450. // If the previously-computed matching expression ignored int overflow or negative zero, those attributes must match
  451. // to be able to CSE this expression
  452. if(intMathExprOnly && !ignoredIntOverflowForCurrentInstr && !ignoredNegativeZeroForCurrentInstr)
  453. {
  454. // Already tried CSE with default attributes
  455. return false;
  456. }
  457. exprAttributes = IntMathExprAttributes(ignoredIntOverflowForCurrentInstr, ignoredNegativeZeroForCurrentInstr);
  458. break;
  459. default:
  460. if(intMathExprOnly)
  461. {
  462. return false;
  463. }
  464. break;
  465. }
  466. if (!this->GetHash(instr, src1Val, src2Val, exprAttributes, &hash))
  467. {
  468. return false;
  469. }
  470. // See if we have a value for that expression
  471. Value ** pVal = block->globOptData.exprToValueMap->Get(hash);
  472. if (pVal == NULL)
  473. {
  474. return false;
  475. }
  476. ValueInfo *valueInfo = NULL;
  477. Sym *symStore = NULL;
  478. Value *val = NULL;
  479. if (instr->GetDst())
  480. {
  481. if (*pVal == NULL)
  482. {
  483. return false;
  484. }
  485. val = *pVal;
  486. // Make sure the array value is still live. We can't CSE something like:
  487. // ... = A[i];
  488. // B[j] = ...;
  489. // ... = A[i];
  490. if (isArray && !block->globOptData.liveArrayValues->Test(hash))
  491. {
  492. return false;
  493. }
  494. // See if the symStore is still valid
  495. valueInfo = val->GetValueInfo();
  496. symStore = valueInfo->GetSymStore();
  497. Value * symStoreVal = NULL;
  498. int32 intConstantValue;
  499. if (!symStore && valueInfo->TryGetIntConstantValue(&intConstantValue))
  500. {
  501. // Handle:
  502. // A[i] = 10;
  503. // ... = A[i];
  504. if (!isArray)
  505. {
  506. return false;
  507. }
  508. if (!valueInfo->IsIntAndLikelyTagged())
  509. {
  510. return false;
  511. }
  512. symStore = GetTaggedIntConstantStackSym(intConstantValue);
  513. }
  514. if(!symStore || !symStore->IsStackSym())
  515. {
  516. return false;
  517. }
  518. symStoreVal = this->FindValue(block->globOptData.symToValueMap, symStore);
  519. if (!symStoreVal || symStoreVal->GetValueNumber() != val->GetValueNumber())
  520. {
  521. return false;
  522. }
  523. val = symStoreVal;
  524. valueInfo = val->GetValueInfo();
  525. }
  526. // Make sure srcs still have same values
  527. if (instr->GetSrc1())
  528. {
  529. if (!src1Val)
  530. {
  531. return false;
  532. }
  533. if (hash.GetSrc1ValueNumber() != src1Val->GetValueNumber())
  534. {
  535. return false;
  536. }
  537. IR::Opnd *src1 = instr->GetSrc1();
  538. if (src1->IsSymOpnd() && src1->AsSymOpnd()->IsPropertySymOpnd())
  539. {
  540. Assert(instr->m_opcode == Js::OpCode::CheckFixedFld);
  541. IR::PropertySymOpnd *propOpnd = src1->AsSymOpnd()->AsPropertySymOpnd();
  542. if (!propOpnd->IsTypeChecked() && !propOpnd->IsRootObjectNonConfigurableFieldLoad())
  543. {
  544. // Require m_CachedTypeChecked for 2 reasons:
  545. // - We may be relying on this instruction to do a type check for a downstream reference.
  546. // - This instruction may have a different inline cache, and thus need to check a different type,
  547. // than the upstream check.
  548. // REVIEW: We could process this differently somehow to get the type check on the next reference.
  549. return false;
  550. }
  551. }
  552. if (instr->GetSrc2())
  553. {
  554. if (!src2Val)
  555. {
  556. return false;
  557. }
  558. if (hash.GetSrc2ValueNumber() != src2Val->GetValueNumber())
  559. {
  560. return false;
  561. }
  562. }
  563. }
  564. bool needsBailOnImplicitCall = false;
  565. // Need implicit call bailouts?
  566. if (this->MayNeedBailOnImplicitCall(instr, src1Val, src2Val))
  567. {
  568. needsBailOnImplicitCall = true;
  569. IR::Opnd *src1 = instr->GetSrc1();
  570. if (instr->m_opcode != Js::OpCode::StElemI_A && instr->m_opcode != Js::OpCode::StElemI_A_Strict
  571. && src1 && src1->IsRegOpnd())
  572. {
  573. StackSym *sym1 = src1->AsRegOpnd()->m_sym;
  574. if (this->IsTypeSpecialized(sym1, block) || block->globOptData.liveInt32Syms->Test(sym1->m_id))
  575. {
  576. IR::Opnd *src2 = instr->GetSrc2();
  577. if (!src2 || src2->IsImmediateOpnd())
  578. {
  579. needsBailOnImplicitCall = false;
  580. }
  581. else if (src2->IsRegOpnd())
  582. {
  583. StackSym *sym2 = src2->AsRegOpnd()->m_sym;
  584. if (this->IsTypeSpecialized(sym2, block) || block->globOptData.liveInt32Syms->Test(sym2->m_id))
  585. {
  586. needsBailOnImplicitCall = false;
  587. }
  588. }
  589. }
  590. }
  591. }
  592. if (needsBailOnImplicitCall)
  593. {
  594. IR::Opnd *src1 = instr->GetSrc1();
  595. if (src1)
  596. {
  597. if (src1->IsRegOpnd())
  598. {
  599. StackSym *varSym1 = src1->AsRegOpnd()->m_sym;
  600. Assert(!varSym1->IsTypeSpec());
  601. if (!block->globOptData.liveFields->Test(varSym1->m_id))
  602. {
  603. return false;
  604. }
  605. IR::Opnd *src2 = instr->GetSrc2();
  606. if (src2 && src2->IsRegOpnd())
  607. {
  608. StackSym *varSym2 = src2->AsRegOpnd()->m_sym;
  609. Assert(!varSym2->IsTypeSpec());
  610. if (!block->globOptData.liveFields->Test(varSym2->m_id))
  611. {
  612. return false;
  613. }
  614. }
  615. }
  616. }
  617. }
  618. // in asmjs we can have a symstore with a different type
  619. // x = HEAPF32[i >> 2]
  620. // y = HEAPI32[i >> 2]
  621. if (instr->GetDst() && (instr->GetDst()->GetType() != symStore->AsStackSym()->GetType()))
  622. {
  623. Assert(GetIsAsmJSFunc());
  624. return false;
  625. }
  626. // SIMD_JS
  627. if (instr->m_opcode == Js::OpCode::ExtendArg_A)
  628. {
  629. // 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.
  630. IR::Opnd *dst = instr->GetDst();
  631. Value *dstVal = this->FindValue(symStore);
  632. this->SetValue(&this->blockData, dstVal, dst);
  633. dst->AsRegOpnd()->m_sym->CopySymAttrs(symStore->AsStackSym());
  634. return false;
  635. }
  636. //
  637. // Success, do the CSE rewrite.
  638. //
  639. #if DBG_DUMP
  640. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  641. {
  642. Output::Print(_u(" --- CSE (%s): "), this->func->GetJITFunctionBody()->GetDisplayName());
  643. instr->Dump();
  644. }
  645. #endif
  646. #if ENABLE_DEBUG_CONFIG_OPTIONS
  647. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
  648. {
  649. Output::Print(_u(" --- CSE (%s): %s\n"), this->func->GetJITFunctionBody()->GetDisplayName(), Js::OpCodeUtil::GetOpCodeName(instr->m_opcode));
  650. }
  651. #endif
  652. this->CaptureByteCodeSymUses(instr);
  653. if (!instr->GetDst())
  654. {
  655. instr->m_opcode = Js::OpCode::Nop;
  656. return true;
  657. }
  658. AnalysisAssert(valueInfo);
  659. IR::Opnd *cseOpnd;
  660. cseOpnd = IR::RegOpnd::New(symStore->AsStackSym(), instr->GetDst()->GetType(), instr->m_func);
  661. cseOpnd->SetValueType(valueInfo->Type());
  662. cseOpnd->SetIsJITOptimizedReg(true);
  663. if (needsBailOnImplicitCall)
  664. {
  665. this->CaptureNoImplicitCallUses(cseOpnd, false);
  666. }
  667. int32 intConstantValue;
  668. if (valueInfo->TryGetIntConstantValue(&intConstantValue) && valueInfo->IsIntAndLikelyTagged())
  669. {
  670. cseOpnd->Free(func);
  671. cseOpnd = IR::AddrOpnd::New(Js::TaggedInt::ToVarUnchecked(intConstantValue), IR::AddrOpndKindConstantVar, instr->m_func);
  672. cseOpnd->SetValueType(valueInfo->Type());
  673. }
  674. *pSrc1Val = val;
  675. {
  676. // Profiled instructions have data that is interpreted differently based on the op code. Since we're changing the op
  677. // code and due to other similar potential issues, always create a new instr instead of changing the existing one.
  678. IR::Instr *const originalInstr = instr;
  679. instr = IR::Instr::New(Js::OpCode::Ld_A, instr->GetDst(), cseOpnd, instr->m_func);
  680. originalInstr->TransferDstAttributesTo(instr);
  681. block->InsertInstrBefore(instr, originalInstr);
  682. block->RemoveInstr(originalInstr);
  683. }
  684. *pSrc2Val = NULL;
  685. *pSrc1IndirIndexVal = NULL;
  686. return true;
  687. }
  688. void
  689. GlobOpt::ProcessArrayValueKills(IR::Instr *instr)
  690. {
  691. switch (instr->m_opcode)
  692. {
  693. case Js::OpCode::StElemI_A:
  694. case Js::OpCode::StElemI_A_Strict:
  695. case Js::OpCode::DeleteElemI_A:
  696. case Js::OpCode::DeleteElemIStrict_A:
  697. case Js::OpCode::StFld:
  698. case Js::OpCode::StRootFld:
  699. case Js::OpCode::StFldStrict:
  700. case Js::OpCode::StRootFldStrict:
  701. case Js::OpCode::StSlot:
  702. case Js::OpCode::StSlotChkUndecl:
  703. case Js::OpCode::DeleteFld:
  704. case Js::OpCode::DeleteRootFld:
  705. case Js::OpCode::DeleteFldStrict:
  706. case Js::OpCode::DeleteRootFldStrict:
  707. case Js::OpCode::StArrViewElem:
  708. // These array helpers may change A.length (and A[i] could be A.length)...
  709. case Js::OpCode::InlineArrayPush:
  710. case Js::OpCode::InlineArrayPop:
  711. this->blockData.liveArrayValues->ClearAll();
  712. break;
  713. case Js::OpCode::CallDirect:
  714. Assert(instr->GetSrc1());
  715. switch(instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper)
  716. {
  717. // These array helpers may change A[i]
  718. case IR::HelperArray_Reverse:
  719. case IR::HelperArray_Shift:
  720. case IR::HelperArray_Unshift:
  721. case IR::HelperArray_Splice:
  722. this->blockData.liveArrayValues->ClearAll();
  723. break;
  724. }
  725. break;
  726. default:
  727. if (instr->UsesAllFields())
  728. {
  729. this->blockData.liveArrayValues->ClearAll();
  730. }
  731. break;
  732. }
  733. }
  734. bool
  735. GlobOpt::NeedBailOnImplicitCallForCSE(BasicBlock *block, bool isForwardPass)
  736. {
  737. return isForwardPass && block->globOptData.hasCSECandidates;
  738. }
  739. bool
  740. GlobOpt::DoCSE()
  741. {
  742. if (PHASE_OFF(Js::CSEPhase, this->func))
  743. {
  744. return false;
  745. }
  746. if (this->IsLoopPrePass())
  747. {
  748. return false;
  749. }
  750. if (PHASE_FORCE(Js::CSEPhase, this->func))
  751. {
  752. // Force always turn it on
  753. return true;
  754. }
  755. if (!this->DoFieldOpts(this->currentBlock->loop) && !GetIsAsmJSFunc())
  756. {
  757. return false;
  758. }
  759. return true;
  760. }
  761. bool
  762. GlobOpt::CanCSEArrayStore(IR::Instr *instr)
  763. {
  764. IR::Opnd *arrayOpnd = instr->GetDst();
  765. Assert(arrayOpnd->IsIndirOpnd());
  766. IR::RegOpnd *baseOpnd = arrayOpnd->AsIndirOpnd()->GetBaseOpnd();
  767. ValueType baseValueType(baseOpnd->GetValueType());
  768. // Only handle definite arrays for now. Typed Arrays would require truncation of the CSE'd value.
  769. if (!baseValueType.IsArrayOrObjectWithArray())
  770. {
  771. return false;
  772. }
  773. return true;
  774. }
  775. #if DBG_DUMP
  776. void
  777. DumpExpr(ExprHash hash)
  778. {
  779. Output::Print(_u("Opcode: %10s src1Val: %d src2Val: %d\n"), Js::OpCodeUtil::GetOpCodeName(HashToOpCode[(int)hash.GetOpcode()]), hash.GetSrc1ValueNumber(), hash.GetSrc2ValueNumber());
  780. }
  781. #endif