Peeps.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  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. // Peeps::PeepFunc
  7. // Run peeps on this function. For now, it just cleans the redundant reloads
  8. // from the register allocator, and reg/reg movs. The code tracks which sym is
  9. // in which registers on extended basic blocks.
  10. void
  11. Peeps::PeepFunc()
  12. {
  13. bool peepsEnabled = true;
  14. if (PHASE_OFF(Js::PeepsPhase, this->func))
  15. {
  16. peepsEnabled = false;
  17. }
  18. #if defined(_M_IX86) || defined(_M_X64)
  19. // Agen dependency elimination pass
  20. // Since it can reveal load elimination opportunities for the normal peeps pass, we do it separately.
  21. this->peepsAgen.PeepFunc();
  22. #endif
  23. this->peepsMD.Init(this);
  24. // Init regMap
  25. memset(this->regMap, 0, sizeof(this->regMap));
  26. // Scratch field needs to be cleared.
  27. this->func->m_symTable->ClearStackSymScratch();
  28. bool isInHelper = false;
  29. FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func)
  30. {
  31. switch (instr->GetKind())
  32. {
  33. case IR::InstrKindLabel:
  34. case IR::InstrKindProfiledLabel:
  35. {
  36. if (!peepsEnabled)
  37. {
  38. break;
  39. }
  40. // Don't carry any regMap info across label
  41. this->ClearRegMap();
  42. // Remove unreferenced labels
  43. if (instr->AsLabelInstr()->IsUnreferenced())
  44. {
  45. bool peeped;
  46. instrNext = PeepUnreachableLabel(instr->AsLabelInstr(), isInHelper, &peeped);
  47. if(peeped)
  48. {
  49. continue;
  50. }
  51. }
  52. else
  53. {
  54. // Try to peep a previous branch again after dead label blocks are removed. For instance:
  55. // jmp L2
  56. // L3:
  57. // // dead code
  58. // L2:
  59. // L3 is unreferenced, so after that block is removed, the branch-to-next can be removed. After that, if L2 is
  60. // unreferenced and only has fallthrough, it can be removed as well.
  61. IR::Instr *const prevInstr = instr->GetPrevRealInstr();
  62. if(prevInstr->IsBranchInstr())
  63. {
  64. IR::BranchInstr *const branch = prevInstr->AsBranchInstr();
  65. if(branch->IsUnconditional() && !branch->IsMultiBranch() && branch->GetTarget() == instr)
  66. {
  67. bool peeped;
  68. IR::Instr *const branchNext = branch->m_next;
  69. IR::Instr *const branchNextAfterPeep = PeepBranch(branch, &peeped);
  70. if(peeped || branchNextAfterPeep != branchNext)
  71. {
  72. // The peep did something, restart from after the branch
  73. instrNext = branchNextAfterPeep;
  74. continue;
  75. }
  76. }
  77. }
  78. }
  79. isInHelper = instr->AsLabelInstr()->isOpHelper;
  80. if (instrNext->IsLabelInstr())
  81. {
  82. // CLean up double label
  83. instrNext = this->CleanupLabel(instr->AsLabelInstr(), instrNext->AsLabelInstr());
  84. }
  85. #if defined(_M_IX86) || defined(_M_X64)
  86. Assert(instrNext->IsLabelInstr() || instrNext->m_prev->IsLabelInstr());
  87. IR::LabelInstr *const peepCondMoveLabel =
  88. instrNext->IsLabelInstr() ? instrNext->AsLabelInstr() : instrNext->m_prev->AsLabelInstr();
  89. instrNext = PeepCondMove(peepCondMoveLabel, instrNext, isInHelper && peepCondMoveLabel->isOpHelper);
  90. #endif
  91. break;
  92. }
  93. case IR::InstrKindBranch:
  94. if (!peepsEnabled || instr->m_opcode == Js::OpCode::Leave)
  95. {
  96. break;
  97. }
  98. instrNext = Peeps::PeepBranch(instr->AsBranchInstr());
  99. #if defined(_M_IX86) || defined(_M_X64)
  100. Assert(instrNext && instrNext->m_prev);
  101. if (instrNext->m_prev->IsBranchInstr())
  102. {
  103. instrNext = this->HoistSameInstructionAboveSplit(instrNext->m_prev->AsBranchInstr(), instrNext);
  104. }
  105. #endif
  106. break;
  107. case IR::InstrKindPragma:
  108. if (instr->m_opcode == Js::OpCode::Nop)
  109. {
  110. instr->Remove();
  111. }
  112. break;
  113. default:
  114. if (LowererMD::IsAssign(instr))
  115. {
  116. if (!peepsEnabled)
  117. {
  118. break;
  119. }
  120. // Cleanup spill code
  121. instrNext = this->PeepAssign(instr);
  122. }
  123. else if (instr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn
  124. || instr->m_opcode == Js::OpCode::StartCall
  125. || instr->m_opcode == Js::OpCode::LoweredStartCall)
  126. {
  127. // ArgOut/StartCall are normally lowered by the lowering of the associated call instr.
  128. // If the call becomes unreachable, we could end up with an orphan ArgOut or StartCall.
  129. // Just delete these StartCalls
  130. instr->Remove();
  131. }
  132. #if defined(_M_IX86) || defined(_M_X64)
  133. else if (instr->m_opcode == Js::OpCode::MOVSD_ZERO)
  134. {
  135. this->peepsMD.PeepAssign(instr);
  136. IR::Opnd *dst = instr->GetDst();
  137. // Look for explicit reg kills
  138. if (dst && dst->IsRegOpnd())
  139. {
  140. this->ClearReg(dst->AsRegOpnd()->GetReg());
  141. }
  142. }
  143. else if ( (instr->m_opcode == Js::OpCode::INC ) || (instr->m_opcode == Js::OpCode::DEC) )
  144. {
  145. // Check for any of the following patterns which can cause partial flag dependency
  146. //
  147. // Jcc or SHL or SHR or SAR or SHLD(in case of x64)
  148. // Jcc or SHL or SHR or SAR or SHLD(in case of x64) Any Instruction
  149. // INC or DEC INC or DEC
  150. // -------------------------------------------------- OR -----------------------
  151. // INC or DEC INC or DEC
  152. // Jcc or SHL or SHR or SAR or SHLD(in case of x64) Any Instruction
  153. // Jcc or SHL or SHR or SAR or SHLD(in case of x64)
  154. //
  155. // With this optimization if any of the above pattern found, substitute INC/DEC with ADD/SUB respectively.
  156. if (!peepsEnabled)
  157. {
  158. break;
  159. }
  160. if (AutoSystemInfo::Data.IsAtomPlatform() || PHASE_FORCE(Js::AtomPhase, this->func))
  161. {
  162. bool pattern_found=false;
  163. if ( !(instr->IsEntryInstr()) )
  164. {
  165. IR::Instr *prevInstr = instr->GetPrevRealInstr();
  166. if ( IsJccOrShiftInstr(prevInstr) )
  167. {
  168. pattern_found = true;
  169. }
  170. else if ( !(prevInstr->IsEntryInstr()) && IsJccOrShiftInstr(prevInstr->GetPrevRealInstr()) )
  171. {
  172. pattern_found=true;
  173. }
  174. }
  175. if ( !pattern_found && !(instr->IsExitInstr()) )
  176. {
  177. IR::Instr *nextInstr = instr->GetNextRealInstr();
  178. if ( IsJccOrShiftInstr(nextInstr) )
  179. {
  180. pattern_found = true;
  181. }
  182. else if ( !(nextInstr->IsExitInstr() ) && (IsJccOrShiftInstr(nextInstr->GetNextRealInstr())) )
  183. {
  184. pattern_found = true;
  185. }
  186. }
  187. if (pattern_found)
  188. {
  189. IR::IntConstOpnd* constOne = IR::IntConstOpnd::New((IntConstType) 1, TyInt32, instr->m_func);
  190. IR::Instr * addOrSubInstr = IR::Instr::New(Js::OpCode::ADD, instr->GetDst(), instr->GetDst(), constOne, instr->m_func);
  191. if (instr->m_opcode == Js::OpCode::DEC)
  192. {
  193. addOrSubInstr->m_opcode = Js::OpCode::SUB;
  194. }
  195. instr->InsertAfter(addOrSubInstr);
  196. instr->Remove();
  197. instr = addOrSubInstr;
  198. }
  199. }
  200. IR::Opnd *dst = instr->GetDst();
  201. // Look for explicit reg kills
  202. if (dst && dst->IsRegOpnd())
  203. {
  204. this->ClearReg(dst->AsRegOpnd()->GetReg());
  205. }
  206. }
  207. #endif
  208. else
  209. {
  210. if (!peepsEnabled)
  211. {
  212. break;
  213. }
  214. #if defined(_M_IX86) || defined(_M_X64)
  215. instr = this->PeepRedundant(instr);
  216. #endif
  217. IR::Opnd *dst = instr->GetDst();
  218. // Look for explicit reg kills
  219. if (dst && dst->IsRegOpnd())
  220. {
  221. this->ClearReg(dst->AsRegOpnd()->GetReg());
  222. }
  223. // Kill callee-saved regs across calls and other implicit regs
  224. this->peepsMD.ProcessImplicitRegs(instr);
  225. #if defined(_M_IX86) || defined(_M_X64)
  226. if (instr->m_opcode == Js::OpCode::TEST && instr->GetSrc2()->IsIntConstOpnd()
  227. && ((instr->GetSrc2()->AsIntConstOpnd()->GetValue() & 0xFFFFFF00) == 0)
  228. && instr->GetSrc1()->IsRegOpnd() && (LinearScan::GetRegAttribs(instr->GetSrc1()->AsRegOpnd()->GetReg()) & RA_BYTEABLE))
  229. {
  230. // Only support if the branch is JEQ or JNE to ensure we don't look at the sign flag
  231. if (instrNext->IsBranchInstr() &&
  232. (instrNext->m_opcode == Js::OpCode::JNE || instrNext->m_opcode == Js::OpCode::JEQ))
  233. {
  234. instr->GetSrc1()->SetType(TyInt8);
  235. instr->GetSrc2()->SetType(TyInt8);
  236. }
  237. }
  238. if (instr->m_opcode == Js::OpCode::CVTSI2SD)
  239. {
  240. IR::Instr *xorps = IR::Instr::New(Js::OpCode::XORPS, instr->GetDst(), instr->GetDst(), instr->GetDst(), instr->m_func);
  241. instr->InsertBefore(xorps);
  242. }
  243. #endif
  244. }
  245. }
  246. } NEXT_INSTR_IN_FUNC_EDITING;
  247. }
  248. #if defined(_M_IX86) || defined(_M_X64)
  249. // Peeps::IsJccOrShiftInstr()
  250. // Check if instruction is any of the Shift or conditional jump variant
  251. bool
  252. Peeps::IsJccOrShiftInstr(IR::Instr *instr)
  253. {
  254. bool instrFound = (instr->IsBranchInstr() && instr->AsBranchInstr()->IsConditional()) ||
  255. (instr->m_opcode == Js::OpCode::SHL) || (instr->m_opcode == Js::OpCode::SHR) || (instr->m_opcode == Js::OpCode::SAR);
  256. #if defined(_M_X64)
  257. instrFound = instrFound || (instr->m_opcode == Js::OpCode::SHLD);
  258. #endif
  259. return (instrFound);
  260. }
  261. #endif
  262. // Peeps::PeepAssign
  263. // Remove useless MOV reg, reg as well as redundant reloads
  264. IR::Instr *
  265. Peeps::PeepAssign(IR::Instr *assign)
  266. {
  267. IR::Opnd *dst = assign->GetDst();
  268. IR::Opnd *src = assign->GetSrc1();
  269. IR::Instr *instrNext = assign->m_next;
  270. // MOV reg, sym
  271. if (src->IsSymOpnd())
  272. {
  273. AssertMsg(src->AsSymOpnd()->m_sym->IsStackSym(), "Only expect stackSyms at this point");
  274. StackSym *sym = src->AsSymOpnd()->m_sym->AsStackSym();
  275. if (sym->scratch.peeps.reg != RegNOREG)
  276. {
  277. // Found a redundant load
  278. AssertMsg(this->regMap[sym->scratch.peeps.reg] == sym, "Something is wrong...");
  279. assign->ReplaceSrc1(IR::RegOpnd::New(sym, sym->scratch.peeps.reg, src->GetType(), this->func));
  280. src = assign->GetSrc1();
  281. }
  282. else
  283. {
  284. // Keep track of this load
  285. AssertMsg(dst->IsRegOpnd(), "For now, we assume dst = sym means dst is a reg");
  286. RegNum reg = dst->AsRegOpnd()->GetReg();
  287. this->SetReg(reg, sym);
  288. return instrNext;
  289. }
  290. }
  291. if (dst->IsRegOpnd())
  292. {
  293. // MOV reg, reg
  294. // Useless?
  295. if (src->IsRegOpnd() && src->AsRegOpnd()->IsSameReg(dst))
  296. {
  297. assign->Remove();
  298. if (instrNext->m_prev->IsBranchInstr())
  299. {
  300. return this->PeepBranch(instrNext->m_prev->AsBranchInstr());
  301. }
  302. else
  303. {
  304. return instrNext;
  305. }
  306. }
  307. else
  308. {
  309. // We could copy the a of the src, but we don't have
  310. // a way to track 2 regs on the sym... So let's just clear
  311. // the info of the dst.
  312. RegNum dstReg = dst->AsRegOpnd()->GetReg();
  313. this->ClearReg(dstReg);
  314. }
  315. }
  316. else if (dst->IsSymOpnd() && src->IsRegOpnd())
  317. {
  318. // MOV Sym, Reg
  319. // Track this reg
  320. RegNum reg = src->AsRegOpnd()->GetReg();
  321. StackSym *sym = dst->AsSymOpnd()->m_sym->AsStackSym();
  322. this->SetReg(reg, sym);
  323. }
  324. this->peepsMD.PeepAssign(assign);
  325. return instrNext;
  326. }
  327. // Peeps::ClearRegMap
  328. // Empty the regMap.
  329. // Note: might be faster to have a count and exit if zero?
  330. void
  331. Peeps::ClearRegMap()
  332. {
  333. for (RegNum reg = (RegNum)(RegNOREG+1); reg != RegNumCount; reg = (RegNum)(reg+1))
  334. {
  335. this->ClearReg(reg);
  336. }
  337. }
  338. // Peeps::SetReg
  339. // Track that this sym is live in this reg
  340. void
  341. Peeps::SetReg(RegNum reg, StackSym *sym)
  342. {
  343. this->ClearReg(sym->scratch.peeps.reg);
  344. this->ClearReg(reg);
  345. this->regMap[reg] = sym;
  346. sym->scratch.peeps.reg = reg;
  347. }
  348. // Peeps::ClearReg
  349. void
  350. Peeps::ClearReg(RegNum reg)
  351. {
  352. StackSym *sym = this->regMap[reg];
  353. if (sym)
  354. {
  355. AssertMsg(sym->scratch.peeps.reg == reg, "Something is wrong here...");
  356. sym->scratch.peeps.reg = RegNOREG;
  357. this->regMap[reg] = NULL;
  358. }
  359. }
  360. // Peeps::PeepBranch
  361. // Remove branch-to-next
  362. // Invert condBranch/uncondBranch/label
  363. // Retarget branch to branch
  364. IR::Instr *
  365. Peeps::PeepBranch(IR::BranchInstr *branchInstr, bool *const peepedRef)
  366. {
  367. if(peepedRef)
  368. {
  369. *peepedRef = false;
  370. }
  371. IR::LabelInstr *targetInstr = branchInstr->GetTarget();
  372. IR::Instr *instrNext;
  373. if (branchInstr->IsUnconditional())
  374. {
  375. // Cleanup unreachable code after unconditional branch
  376. instrNext = RemoveDeadBlock(branchInstr->m_next);
  377. }
  378. instrNext = branchInstr->GetNextRealInstrOrLabel();
  379. if (instrNext != NULL && instrNext->IsLabelInstr())
  380. {
  381. //
  382. // Remove branch-to-next
  383. //
  384. if (targetInstr == instrNext)
  385. {
  386. if (!branchInstr->IsLowered())
  387. {
  388. if (branchInstr->HasAnyImplicitCalls())
  389. {
  390. Assert(!branchInstr->m_func->GetJnFunction()->GetIsAsmjsMode());
  391. // if (x > y) might trigger a call to valueof() or something for x and y.
  392. // We can't just delete them.
  393. Js::OpCode newOpcode;
  394. switch(branchInstr->m_opcode)
  395. {
  396. case Js::OpCode::BrEq_A:
  397. case Js::OpCode::BrNeq_A:
  398. case Js::OpCode::BrNotEq_A:
  399. case Js::OpCode::BrNotNeq_A:
  400. newOpcode = Js::OpCode::DeadBrEqual;
  401. break;
  402. case Js::OpCode::BrSrEq_A:
  403. case Js::OpCode::BrSrNeq_A:
  404. case Js::OpCode::BrSrNotEq_A:
  405. case Js::OpCode::BrSrNotNeq_A:
  406. newOpcode = Js::OpCode::DeadBrSrEqual;
  407. break;
  408. case Js::OpCode::BrGe_A:
  409. case Js::OpCode::BrGt_A:
  410. case Js::OpCode::BrLe_A:
  411. case Js::OpCode::BrLt_A:
  412. case Js::OpCode::BrNotGe_A:
  413. case Js::OpCode::BrNotGt_A:
  414. case Js::OpCode::BrNotLe_A:
  415. case Js::OpCode::BrNotLt_A:
  416. case Js::OpCode::BrUnGe_A:
  417. case Js::OpCode::BrUnGt_A:
  418. case Js::OpCode::BrUnLe_A:
  419. case Js::OpCode::BrUnLt_A:
  420. newOpcode = Js::OpCode::DeadBrRelational;
  421. break;
  422. case Js::OpCode::BrOnHasProperty:
  423. case Js::OpCode::BrOnNoProperty:
  424. newOpcode = Js::OpCode::DeadBrOnHasProperty;
  425. break;
  426. default:
  427. Assert(UNREACHED);
  428. newOpcode = Js::OpCode::Nop;
  429. }
  430. IR::Instr *newInstr = IR::Instr::New(newOpcode, branchInstr->m_func);
  431. newInstr->SetSrc1(branchInstr->GetSrc1());
  432. newInstr->SetSrc2(branchInstr->GetSrc2());
  433. branchInstr->InsertBefore(newInstr);
  434. newInstr->SetByteCodeOffset(branchInstr);
  435. }
  436. else if (branchInstr->GetSrc1() && !branchInstr->GetSrc2())
  437. {
  438. // We only have cases with one src
  439. Assert(branchInstr->GetSrc1()->IsRegOpnd());
  440. IR::RegOpnd *regSrc = branchInstr->GetSrc1()->AsRegOpnd();
  441. StackSym *symSrc = regSrc->GetStackSym();
  442. if (symSrc->HasByteCodeRegSlot())
  443. {
  444. // No side-effects to worry about, but need to insert bytecodeUse.
  445. IR::ByteCodeUsesInstr *byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(branchInstr->m_func);
  446. byteCodeUsesInstr->SetByteCodeOffset(branchInstr);
  447. byteCodeUsesInstr->byteCodeUpwardExposedUsed = JitAnew(branchInstr->m_func->m_alloc, BVSparse<JitArenaAllocator>, branchInstr->m_func->m_alloc);
  448. byteCodeUsesInstr->byteCodeUpwardExposedUsed->Set(regSrc->GetStackSym()->m_id);
  449. branchInstr->InsertBefore(byteCodeUsesInstr);
  450. }
  451. }
  452. }
  453. // Note: if branch is conditional, we have a dead test/cmp left behind...
  454. if(peepedRef)
  455. {
  456. *peepedRef = true;
  457. }
  458. branchInstr->Remove();
  459. if (targetInstr->IsUnreferenced())
  460. {
  461. // We may have exposed an unreachable label by deleting the branch
  462. instrNext = Peeps::PeepUnreachableLabel(targetInstr, false);
  463. }
  464. else
  465. {
  466. instrNext = targetInstr;
  467. }
  468. IR::Instr *instrPrev = instrNext->GetPrevRealInstrOrLabel();
  469. if (instrPrev->IsBranchInstr())
  470. {
  471. // The branch removal could have exposed a branch to next opportunity.
  472. return Peeps::PeepBranch(instrPrev->AsBranchInstr());
  473. }
  474. return instrNext;
  475. }
  476. }
  477. else if (branchInstr->IsConditional())
  478. {
  479. AnalysisAssert(instrNext);
  480. if (instrNext->IsBranchInstr()
  481. && instrNext->AsBranchInstr()->IsUnconditional()
  482. && targetInstr == instrNext->AsBranchInstr()->GetNextRealInstrOrLabel()
  483. && !instrNext->AsBranchInstr()->IsMultiBranch())
  484. {
  485. //
  486. // Invert condBranch/uncondBranch/label:
  487. //
  488. // JCC L1 JinvCC L3
  489. // JMP L3 =>
  490. // L1:
  491. IR::BranchInstr *uncondBranch = instrNext->AsBranchInstr();
  492. if (branchInstr->IsLowered())
  493. {
  494. LowererMD::InvertBranch(branchInstr);
  495. }
  496. else
  497. {
  498. branchInstr->Invert();
  499. }
  500. targetInstr = uncondBranch->GetTarget();
  501. branchInstr->SetTarget(targetInstr);
  502. if (targetInstr->IsUnreferenced())
  503. {
  504. Peeps::PeepUnreachableLabel(targetInstr, false);
  505. }
  506. uncondBranch->Remove();
  507. return PeepBranch(branchInstr, peepedRef);
  508. }
  509. }
  510. if(branchInstr->IsMultiBranch())
  511. {
  512. IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
  513. multiBranchInstr->UpdateMultiBrLabels([=](IR::LabelInstr * targetInstr) -> IR::LabelInstr *
  514. {
  515. IR::LabelInstr * labelInstr = RetargetBrToBr(branchInstr, targetInstr);
  516. return labelInstr;
  517. });
  518. }
  519. else
  520. {
  521. RetargetBrToBr(branchInstr, targetInstr);
  522. }
  523. return branchInstr->m_next;
  524. }
  525. #if defined(_M_IX86) || defined(_M_X64)
  526. //
  527. // For conditional branch JE $LTarget, if both target and fallthrough branch has the same
  528. // instruction B, hoist it up and tail dup target branch:
  529. //
  530. // A <unconditional branch>
  531. // JE $LTarget $LTarget:
  532. // B B
  533. // ... JMP $L2
  534. //
  535. //====> hoist B up: move B up from fallthrough branch, remove B in target branch, retarget to $L2
  536. // $LTarget to $L2
  537. //
  538. // A <unconditional branch>
  539. // B $LTarget:
  540. // JE $L2 JMP $L2
  541. // ...
  542. //
  543. //====> now $LTarget becomes to be an empty BB, which can be removed if it's an unreferenced label
  544. //
  545. // A
  546. // B
  547. // JE $L2
  548. // ...
  549. //
  550. //====> Note B will be hoist above compare instruction A if there are no dependency between A and B
  551. //
  552. // B
  553. // A (cmp instr)
  554. // JE $L2
  555. // ...
  556. //
  557. IR::Instr *
  558. Peeps::HoistSameInstructionAboveSplit(IR::BranchInstr *branchInstr, IR::Instr *instrNext)
  559. {
  560. Assert(branchInstr);
  561. if (!branchInstr->IsConditional() || branchInstr->IsMultiBranch() || !branchInstr->IsLowered())
  562. {
  563. return instrNext; // this optimization only applies to single conditional branch
  564. }
  565. IR::LabelInstr *targetLabel = branchInstr->GetTarget();
  566. Assert(targetLabel);
  567. // give up if there are other branch entries to the target label
  568. if (targetLabel->labelRefs.Count() > 1)
  569. {
  570. return instrNext;
  571. }
  572. // Give up if previous instruction before target label has fallthrough, cannot hoist up
  573. IR::Instr *targetPrev = targetLabel->GetPrevRealInstrOrLabel();
  574. Assert(targetPrev);
  575. if (targetPrev->HasFallThrough())
  576. {
  577. return instrNext;
  578. }
  579. IR::Instr *instrSetCondition = NULL;
  580. IR::Instr *branchPrev = branchInstr->GetPrevRealInstrOrLabel();
  581. while (branchPrev && !branchPrev->StartsBasicBlock())
  582. {
  583. if (!instrSetCondition && EncoderMD::SetsConditionCode(branchPrev))
  584. { // located compare instruction for the branch
  585. instrSetCondition = branchPrev;
  586. }
  587. branchPrev = branchPrev->GetPrevRealInstrOrLabel(); // keep looking previous instr in BB
  588. }
  589. if (branchPrev && branchPrev->IsLabelInstr() && branchPrev->AsLabelInstr()->isOpHelper)
  590. { // don't apply the optimization when branch is in helper section
  591. return instrNext;
  592. }
  593. if (!instrSetCondition)
  594. { // give up if we cannot find the compare instruction in the BB, should be very rare
  595. return instrNext;
  596. }
  597. Assert(instrSetCondition);
  598. bool hoistAboveSetConditionInstr = false;
  599. if (instrSetCondition == branchInstr->GetPrevRealInstrOrLabel())
  600. { // if compare instruction is right before branch instruction, we can hoist above cmp instr
  601. hoistAboveSetConditionInstr = true;
  602. } // otherwise we hoist the identical instructions above conditional branch split only
  603. IR::Instr *instr = branchInstr->GetNextRealInstrOrLabel();
  604. IR::Instr *targetInstr = targetLabel->GetNextRealInstrOrLabel();
  605. IR::Instr *branchNextInstr = NULL;
  606. IR::Instr *targetNextInstr = NULL;
  607. IR::Instr *instrHasHoisted = NULL;
  608. Assert(instr && targetInstr);
  609. while (!instr->EndsBasicBlock() && !instr->IsLabelInstr() && instr->IsEqual(targetInstr) &&
  610. !EncoderMD::UsesConditionCode(instr) && !EncoderMD::SetsConditionCode(instr) &&
  611. !this->peepsAgen.DependentInstrs(instrSetCondition, instr))
  612. {
  613. branchNextInstr = instr->GetNextRealInstrOrLabel();
  614. targetNextInstr = targetInstr->GetNextRealInstrOrLabel();
  615. instr->Unlink(); // hoist up instr in fallthrough branch
  616. if (hoistAboveSetConditionInstr)
  617. {
  618. instrSetCondition->InsertBefore(instr); // hoist above compare instruction
  619. }
  620. else
  621. {
  622. branchInstr->InsertBefore(instr); // hoist above branch split
  623. }
  624. targetInstr->Remove(); // remove the same instruction in target branch
  625. if (!instrHasHoisted)
  626. instrHasHoisted = instr; // points to the first hoisted instruction
  627. instr = branchNextInstr;
  628. targetInstr = targetNextInstr;
  629. Assert(instr && targetInstr);
  630. }
  631. if (instrHasHoisted)
  632. { // instructions have been hoisted, now check tail branch to see if it can be duplicated
  633. if (targetInstr->IsBranchInstr())
  634. {
  635. IR::BranchInstr *tailBranch = targetInstr->AsBranchInstr();
  636. if (tailBranch->IsUnconditional() && !tailBranch->IsMultiBranch())
  637. { // target can be replaced since tail branch is a single unconditional jmp
  638. branchInstr->ReplaceTarget(targetLabel, tailBranch->GetTarget());
  639. }
  640. // now targeLabel is an empty Basic Block, remove it if it's not referenced
  641. if (targetLabel->IsUnreferenced())
  642. {
  643. Peeps::PeepUnreachableLabel(targetLabel, targetLabel->isOpHelper);
  644. }
  645. }
  646. return instrHasHoisted;
  647. }
  648. return instrNext;
  649. }
  650. #endif
  651. IR::LabelInstr *
  652. Peeps::RetargetBrToBr(IR::BranchInstr *branchInstr, IR::LabelInstr * targetInstr)
  653. {
  654. AnalysisAssert(targetInstr);
  655. IR::Instr *targetInstrNext = targetInstr->GetNextRealInstr();
  656. AnalysisAssertMsg(targetInstrNext, "GetNextRealInstr() failed to get next target");
  657. // Removing branch to branch breaks some lexical assumptions about loop in sccliveness/linearscan/second chance.
  658. if (!branchInstr->IsLowered())
  659. {
  660. return targetInstr;
  661. }
  662. //
  663. // Retarget branch-to-branch
  664. //
  665. #if DBG
  666. uint counter = 0;
  667. #endif
  668. IR::LabelInstr *lastLoopTop = NULL;
  669. while (targetInstrNext->IsBranchInstr() && targetInstrNext->AsBranchInstr()->IsUnconditional())
  670. {
  671. IR::BranchInstr *branchAtTarget = targetInstrNext->AsBranchInstr();
  672. // We don't want to skip the loop entry, unless we're right before the encoder
  673. if (targetInstr->m_isLoopTop && !branchAtTarget->IsLowered())
  674. {
  675. break;
  676. }
  677. if (targetInstr->m_isLoopTop)
  678. {
  679. if (targetInstr == lastLoopTop)
  680. {
  681. // We are back to a loopTop already visited.
  682. // Looks like an infinite loop somewhere...
  683. break;
  684. }
  685. lastLoopTop = targetInstr;
  686. }
  687. #if DBG
  688. if (!branchInstr->IsMultiBranch() && branchInstr->GetTarget()->m_noHelperAssert && !branchAtTarget->IsMultiBranch())
  689. {
  690. branchAtTarget->GetTarget()->m_noHelperAssert = true;
  691. }
  692. AssertMsg(++counter < 10000, "We should not be looping this many times!");
  693. #endif
  694. IR::LabelInstr * reTargetLabel = branchAtTarget->GetTarget();
  695. AnalysisAssert(reTargetLabel);
  696. if (targetInstr == reTargetLabel)
  697. {
  698. // Infinite loop.
  699. // JCC $L1
  700. // L1:
  701. // JMP $L1
  702. break;
  703. }
  704. if(branchInstr->IsMultiBranch())
  705. {
  706. IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
  707. multiBranchInstr->ChangeLabelRef(targetInstr, reTargetLabel);
  708. }
  709. else
  710. {
  711. branchInstr->SetTarget(reTargetLabel);
  712. }
  713. if (targetInstr->IsUnreferenced())
  714. {
  715. Peeps::PeepUnreachableLabel(targetInstr, false);
  716. }
  717. targetInstr = reTargetLabel;
  718. targetInstrNext = targetInstr->GetNextRealInstr();
  719. }
  720. return targetInstr;
  721. }
  722. IR::Instr *
  723. Peeps::PeepUnreachableLabel(IR::LabelInstr *deadLabel, const bool isInHelper, bool *const peepedRef)
  724. {
  725. Assert(deadLabel);
  726. Assert(deadLabel->IsUnreferenced());
  727. IR::Instr *prevFallthroughInstr = deadLabel;
  728. do
  729. {
  730. prevFallthroughInstr = prevFallthroughInstr->GetPrevRealInstrOrLabel();
  731. // The previous dead label may have been kept around due to a StatementBoundary, see comment in RemoveDeadBlock.
  732. } while(prevFallthroughInstr->IsLabelInstr() && prevFallthroughInstr->AsLabelInstr()->IsUnreferenced());
  733. IR::Instr *instrReturn;
  734. bool removeLabel;
  735. // If code is now unreachable, delete block
  736. if (!prevFallthroughInstr->HasFallThrough())
  737. {
  738. bool wasStatementBoundaryKeptInDeadBlock = false;
  739. instrReturn = RemoveDeadBlock(deadLabel->m_next, &wasStatementBoundaryKeptInDeadBlock);
  740. // Remove label only if we didn't have to keep last StatementBoundary in the dead block,
  741. // see comment in RemoveDeadBlock.
  742. removeLabel = !wasStatementBoundaryKeptInDeadBlock;
  743. if(peepedRef)
  744. {
  745. *peepedRef = true;
  746. }
  747. }
  748. else
  749. {
  750. instrReturn = deadLabel->m_next;
  751. removeLabel =
  752. deadLabel->isOpHelper == isInHelper
  753. #if DBG
  754. && !deadLabel->m_noHelperAssert
  755. #endif
  756. ;
  757. if(peepedRef)
  758. {
  759. *peepedRef = removeLabel;
  760. }
  761. }
  762. if (removeLabel && deadLabel->IsUnreferenced())
  763. {
  764. deadLabel->Remove();
  765. }
  766. return instrReturn;
  767. }
  768. IR::Instr *
  769. Peeps::CleanupLabel(IR::LabelInstr * instr, IR::LabelInstr * instrNext)
  770. {
  771. IR::Instr * returnInstr;
  772. IR::LabelInstr * labelToRemove;
  773. IR::LabelInstr * labelToKeep;
  774. // Just for dump, always keep loop top labels
  775. // We also can remove label that has non branch references
  776. if (instrNext->m_isLoopTop || instrNext->m_hasNonBranchRef)
  777. {
  778. if (instr->m_isLoopTop || instr->m_hasNonBranchRef)
  779. {
  780. // Don't remove loop top labels or labels with non branch references
  781. return instrNext;
  782. }
  783. labelToRemove = instr;
  784. labelToKeep = instrNext;
  785. returnInstr = instrNext;
  786. }
  787. else
  788. {
  789. labelToRemove = instrNext;
  790. labelToKeep = instr;
  791. returnInstr = instrNext->m_next;
  792. }
  793. while (!labelToRemove->labelRefs.Empty())
  794. {
  795. bool replaced = labelToRemove->labelRefs.Head()->ReplaceTarget(labelToRemove, labelToKeep);
  796. Assert(replaced);
  797. }
  798. if (labelToRemove->isOpHelper)
  799. {
  800. labelToKeep->isOpHelper = true;
  801. #if DBG
  802. if (labelToRemove->m_noHelperAssert)
  803. {
  804. labelToKeep->m_noHelperAssert = true;
  805. }
  806. #endif
  807. }
  808. labelToRemove->Remove();
  809. return returnInstr;
  810. }
  811. //
  812. // Removes instrs starting from one specified by the 'instr' parameter.
  813. // Keeps last statement boundary in the whole block to remove.
  814. // Stops at label or exit instr.
  815. // Return value:
  816. // - 1st instr that is label or end instr, except the case when forceRemoveFirstInstr is true, in which case
  817. // we start checking for exit loop condition from next instr.
  818. // Notes:
  819. // - if wasStmtBoundaryKeptInDeadBlock is not NULL, it receives true when we didn't remove last
  820. // StatementBoundary pragma instr as otherwise it would be non-helper/helper move of the pragma instr.
  821. // If there was no stmt boundary or last stmt boundary moved to after next label, that would receive false.
  822. //
  823. IR::Instr *Peeps::RemoveDeadBlock(IR::Instr *instr, bool* wasStmtBoundaryKeptInDeadBlock /* = nullptr */)
  824. {
  825. IR::Instr* lastStatementBoundary = nullptr;
  826. while (instr && !instr->IsLabelInstr() && !instr->IsExitInstr())
  827. {
  828. IR::Instr *deadInstr = instr;
  829. instr = instr->m_next;
  830. if (deadInstr->IsPragmaInstr() && deadInstr->m_opcode == Js::OpCode::StatementBoundary)
  831. {
  832. if (lastStatementBoundary)
  833. {
  834. //Its enough if we keep latest statement boundary. Rest are dead anyway.
  835. lastStatementBoundary->Remove();
  836. }
  837. lastStatementBoundary = deadInstr;
  838. }
  839. else
  840. {
  841. deadInstr->Remove();
  842. }
  843. }
  844. // Do not let StatementBoundary to move across non-helper and helper blocks, very important under debugger:
  845. // if we let that happen, helper block can be moved to the end of the func so that statement maps will miss one statement.
  846. // Issues can be when (normally, StatementBoundary should never belong to a helper label):
  847. // - if we remove the label and prev label is a helper, StatementBoundary will be moved inside helper.
  848. // - if we move StatementBoundary under next label which is a helper, same problem again.
  849. bool canMoveStatementBoundaryUnderNextLabel = instr && instr->IsLabelInstr() && !instr->AsLabelInstr()->isOpHelper;
  850. if (lastStatementBoundary && canMoveStatementBoundaryUnderNextLabel)
  851. {
  852. lastStatementBoundary->Unlink();
  853. instr->InsertAfter(lastStatementBoundary);
  854. }
  855. if (wasStmtBoundaryKeptInDeadBlock)
  856. {
  857. *wasStmtBoundaryKeptInDeadBlock = lastStatementBoundary && !canMoveStatementBoundaryUnderNextLabel;
  858. }
  859. return instr;
  860. }
  861. // Shared code for x86 and amd64
  862. #if defined(_M_IX86) || defined(_M_X64)
  863. IR::Instr *
  864. Peeps::PeepRedundant(IR::Instr *instr)
  865. {
  866. IR::Instr *retInstr = instr;
  867. if (instr->m_opcode == Js::OpCode::ADD || instr->m_opcode == Js::OpCode::SUB || instr->m_opcode == Js::OpCode::OR)
  868. {
  869. Assert(instr->GetSrc1() && instr->GetSrc2());
  870. if( (instr->GetSrc2()->IsIntConstOpnd() && instr->GetSrc2()->AsIntConstOpnd()->GetValue() == 0))
  871. {
  872. // remove instruction
  873. retInstr = instr->m_next;
  874. instr->Remove();
  875. }
  876. }
  877. #if _M_IX86
  878. RegNum edx = RegEDX;
  879. #else
  880. RegNum edx = RegRDX;
  881. #endif
  882. if (instr->m_opcode == Js::OpCode::NOP && instr->GetDst() != NULL
  883. && instr->GetDst()->IsRegOpnd() && instr->GetDst()->AsRegOpnd()->GetReg() == edx)
  884. {
  885. // dummy def used for non-32bit ovf check for IMUL
  886. // check edx is not killed between IMUL and edx = NOP, then remove the NOP
  887. bool found = false;
  888. IR::Instr *nopInstr = instr;
  889. do
  890. {
  891. instr = instr->GetPrevRealInstrOrLabel();
  892. if (instr->m_opcode == Js::OpCode::IMUL)
  893. {
  894. found = true;
  895. break;
  896. }
  897. } while(!instr->StartsBasicBlock());
  898. if (found)
  899. {
  900. retInstr = nopInstr->m_next;
  901. nopInstr->Remove();
  902. }
  903. else
  904. {
  905. instr = nopInstr;
  906. do
  907. {
  908. instr = instr->GetNextRealInstrOrLabel();
  909. if (instr->m_opcode == Js::OpCode::DIV)
  910. {
  911. found = true;
  912. retInstr = nopInstr->m_next;
  913. nopInstr->Remove();
  914. break;
  915. }
  916. } while (!instr->EndsBasicBlock());
  917. AssertMsg(found, "edx = NOP without an IMUL or DIV");
  918. }
  919. }
  920. return retInstr;
  921. }
  922. /*
  923. Work backwards from the label instruction to look for this pattern:
  924. Jcc $Label
  925. Mov
  926. Mov
  927. ..
  928. Mov
  929. Label:
  930. If found, we remove the Jcc, convert MOVs to CMOVcc and remove the label if unreachable.
  931. */
  932. IR::Instr*
  933. Peeps::PeepCondMove(IR::LabelInstr *labelInstr, IR::Instr *nextInstr, const bool isInHelper)
  934. {
  935. IR::Instr *instr = labelInstr->GetPrevRealInstrOrLabel();
  936. Js::OpCode newOpCode;
  937. // Check if BB is all MOVs with both RegOpnd
  938. while(instr->m_opcode == Js::OpCode::MOV)
  939. {
  940. if (!instr->GetSrc1()->IsRegOpnd() || !instr->GetDst()->IsRegOpnd())
  941. return nextInstr;
  942. instr = instr->GetPrevRealInstrOrLabel();
  943. }
  944. // Did we hit a conditional branch ?
  945. if (instr->IsBranchInstr() && instr->AsBranchInstr()->IsConditional() &&
  946. !instr->AsBranchInstr()->IsMultiBranch() &&
  947. instr->AsBranchInstr()->GetTarget() == labelInstr &&
  948. instr->m_opcode != Js::OpCode::Leave)
  949. {
  950. IR::BranchInstr *brInstr = instr->AsBranchInstr();
  951. // Get the correct CMOVcc
  952. switch(brInstr->m_opcode)
  953. {
  954. case Js::OpCode::JA:
  955. newOpCode = Js::OpCode::CMOVBE;
  956. break;
  957. case Js::OpCode::JAE:
  958. newOpCode = Js::OpCode::CMOVB;
  959. break;
  960. case Js::OpCode::JB:
  961. newOpCode = Js::OpCode::CMOVAE;
  962. break;
  963. case Js::OpCode::JBE:
  964. newOpCode = Js::OpCode::CMOVA;
  965. break;
  966. case Js::OpCode::JEQ:
  967. newOpCode = Js::OpCode::CMOVNE;
  968. break;
  969. case Js::OpCode::JNE:
  970. newOpCode = Js::OpCode::CMOVE;
  971. break;
  972. case Js::OpCode::JNP:
  973. newOpCode = Js::OpCode::CMOVP;
  974. break;
  975. case Js::OpCode::JLT:
  976. newOpCode = Js::OpCode::CMOVGE;
  977. break;
  978. case Js::OpCode::JLE:
  979. newOpCode = Js::OpCode::CMOVG;
  980. break;
  981. case Js::OpCode::JGT:
  982. newOpCode = Js::OpCode::CMOVLE;
  983. break;
  984. case Js::OpCode::JGE:
  985. newOpCode = Js::OpCode::CMOVL;
  986. break;
  987. case Js::OpCode::JNO:
  988. newOpCode = Js::OpCode::CMOVO;
  989. break;
  990. case Js::OpCode::JO:
  991. newOpCode = Js::OpCode::CMOVNO;
  992. break;
  993. case Js::OpCode::JP:
  994. newOpCode = Js::OpCode::CMOVNP;
  995. break;
  996. case Js::OpCode::JNSB:
  997. newOpCode = Js::OpCode::CMOVS;
  998. break;
  999. case Js::OpCode::JSB:
  1000. newOpCode = Js::OpCode::CMOVNS;
  1001. break;
  1002. default:
  1003. Assert(UNREACHED);
  1004. __assume(UNREACHED);
  1005. }
  1006. // convert the entire block to CMOVs
  1007. instr = brInstr->GetNextRealInstrOrLabel();
  1008. while(instr != labelInstr)
  1009. {
  1010. instr->m_opcode = newOpCode;
  1011. instr = instr->GetNextRealInstrOrLabel();
  1012. }
  1013. // remove the Jcc
  1014. brInstr->Remove();
  1015. // We may have exposed an unreachable label by deleting the branch
  1016. if (labelInstr->IsUnreferenced())
  1017. return Peeps::PeepUnreachableLabel(labelInstr, isInHelper);
  1018. }
  1019. return nextInstr;
  1020. }
  1021. #endif