Peeps.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  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, instr->GetDst()->GetType(), 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() && src->AsSymOpnd()->m_offset == 0)
  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() && dst->AsSymOpnd()->m_offset == 0 && 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(reg);
  344. if (sym->m_isClosureSym)
  345. {
  346. return;
  347. }
  348. this->ClearReg(sym->scratch.peeps.reg);
  349. this->regMap[reg] = sym;
  350. sym->scratch.peeps.reg = reg;
  351. }
  352. // Peeps::ClearReg
  353. void
  354. Peeps::ClearReg(RegNum reg)
  355. {
  356. StackSym *sym = this->regMap[reg];
  357. if (sym)
  358. {
  359. AssertMsg(sym->scratch.peeps.reg == reg, "Something is wrong here...");
  360. sym->scratch.peeps.reg = RegNOREG;
  361. this->regMap[reg] = NULL;
  362. }
  363. }
  364. // Peeps::PeepBranch
  365. // Remove branch-to-next
  366. // Invert condBranch/uncondBranch/label
  367. // Retarget branch to branch
  368. IR::Instr *
  369. Peeps::PeepBranch(IR::BranchInstr *branchInstr, bool *const peepedRef)
  370. {
  371. if(peepedRef)
  372. {
  373. *peepedRef = false;
  374. }
  375. IR::LabelInstr *targetInstr = branchInstr->GetTarget();
  376. IR::Instr *instrNext;
  377. if (branchInstr->IsUnconditional())
  378. {
  379. // Cleanup unreachable code after unconditional branch
  380. instrNext = RemoveDeadBlock(branchInstr->m_next);
  381. }
  382. instrNext = branchInstr->GetNextRealInstrOrLabel();
  383. if (instrNext != NULL && instrNext->IsLabelInstr())
  384. {
  385. //
  386. // Remove branch-to-next
  387. //
  388. if (targetInstr == instrNext)
  389. {
  390. if (!branchInstr->IsLowered())
  391. {
  392. if (branchInstr->HasAnyImplicitCalls())
  393. {
  394. Assert(!branchInstr->m_func->GetJITFunctionBody()->IsAsmJsMode());
  395. // if (x > y) might trigger a call to valueof() or something for x and y.
  396. // We can't just delete them.
  397. Js::OpCode newOpcode;
  398. switch(branchInstr->m_opcode)
  399. {
  400. case Js::OpCode::BrEq_A:
  401. case Js::OpCode::BrNeq_A:
  402. case Js::OpCode::BrNotEq_A:
  403. case Js::OpCode::BrNotNeq_A:
  404. newOpcode = Js::OpCode::DeadBrEqual;
  405. break;
  406. case Js::OpCode::BrSrEq_A:
  407. case Js::OpCode::BrSrNeq_A:
  408. case Js::OpCode::BrSrNotEq_A:
  409. case Js::OpCode::BrSrNotNeq_A:
  410. newOpcode = Js::OpCode::DeadBrSrEqual;
  411. break;
  412. case Js::OpCode::BrGe_A:
  413. case Js::OpCode::BrGt_A:
  414. case Js::OpCode::BrLe_A:
  415. case Js::OpCode::BrLt_A:
  416. case Js::OpCode::BrNotGe_A:
  417. case Js::OpCode::BrNotGt_A:
  418. case Js::OpCode::BrNotLe_A:
  419. case Js::OpCode::BrNotLt_A:
  420. case Js::OpCode::BrUnGe_A:
  421. case Js::OpCode::BrUnGt_A:
  422. case Js::OpCode::BrUnLe_A:
  423. case Js::OpCode::BrUnLt_A:
  424. newOpcode = Js::OpCode::DeadBrRelational;
  425. break;
  426. case Js::OpCode::BrOnHasProperty:
  427. case Js::OpCode::BrOnNoProperty:
  428. newOpcode = Js::OpCode::DeadBrOnHasProperty;
  429. break;
  430. default:
  431. Assert(UNREACHED);
  432. newOpcode = Js::OpCode::Nop;
  433. }
  434. IR::Instr *newInstr = IR::Instr::New(newOpcode, branchInstr->m_func);
  435. newInstr->SetSrc1(branchInstr->GetSrc1());
  436. newInstr->SetSrc2(branchInstr->GetSrc2());
  437. branchInstr->InsertBefore(newInstr);
  438. newInstr->SetByteCodeOffset(branchInstr);
  439. }
  440. else if (branchInstr->GetSrc1() && !branchInstr->GetSrc2())
  441. {
  442. // We only have cases with one src
  443. Assert(branchInstr->GetSrc1()->IsRegOpnd());
  444. IR::RegOpnd *regSrc = branchInstr->GetSrc1()->AsRegOpnd();
  445. StackSym *symSrc = regSrc->GetStackSym();
  446. if (symSrc->HasByteCodeRegSlot() && !regSrc->GetIsJITOptimizedReg())
  447. {
  448. // No side-effects to worry about, but need to insert bytecodeUse.
  449. IR::ByteCodeUsesInstr *byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(branchInstr);
  450. byteCodeUsesInstr->Set(regSrc);
  451. branchInstr->InsertBefore(byteCodeUsesInstr);
  452. }
  453. }
  454. }
  455. // Note: if branch is conditional, we have a dead test/cmp left behind...
  456. if(peepedRef)
  457. {
  458. *peepedRef = true;
  459. }
  460. branchInstr->Remove();
  461. if (targetInstr->IsUnreferenced())
  462. {
  463. // We may have exposed an unreachable label by deleting the branch
  464. instrNext = Peeps::PeepUnreachableLabel(targetInstr, false);
  465. }
  466. else
  467. {
  468. instrNext = targetInstr;
  469. }
  470. IR::Instr *instrPrev = instrNext->GetPrevRealInstrOrLabel();
  471. if (instrPrev->IsBranchInstr())
  472. {
  473. // The branch removal could have exposed a branch to next opportunity.
  474. return Peeps::PeepBranch(instrPrev->AsBranchInstr());
  475. }
  476. return instrNext;
  477. }
  478. }
  479. else if (branchInstr->IsConditional())
  480. {
  481. AnalysisAssert(instrNext);
  482. if (instrNext->IsBranchInstr()
  483. && instrNext->AsBranchInstr()->IsUnconditional()
  484. && targetInstr == instrNext->AsBranchInstr()->GetNextRealInstrOrLabel()
  485. && !instrNext->AsBranchInstr()->IsMultiBranch())
  486. {
  487. //
  488. // Invert condBranch/uncondBranch/label:
  489. //
  490. // JCC L1 JinvCC L3
  491. // JMP L3 =>
  492. // L1:
  493. IR::BranchInstr *uncondBranch = instrNext->AsBranchInstr();
  494. if (branchInstr->IsLowered())
  495. {
  496. LowererMD::InvertBranch(branchInstr);
  497. }
  498. else
  499. {
  500. branchInstr->Invert();
  501. }
  502. targetInstr = uncondBranch->GetTarget();
  503. branchInstr->SetTarget(targetInstr);
  504. if (targetInstr->IsUnreferenced())
  505. {
  506. Peeps::PeepUnreachableLabel(targetInstr, false);
  507. }
  508. uncondBranch->Remove();
  509. return PeepBranch(branchInstr, peepedRef);
  510. }
  511. }
  512. if(branchInstr->IsMultiBranch())
  513. {
  514. IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
  515. multiBranchInstr->UpdateMultiBrLabels([=](IR::LabelInstr * targetInstr) -> IR::LabelInstr *
  516. {
  517. IR::LabelInstr * labelInstr = RetargetBrToBr(branchInstr, targetInstr);
  518. return labelInstr;
  519. });
  520. }
  521. else
  522. {
  523. RetargetBrToBr(branchInstr, targetInstr);
  524. }
  525. return branchInstr->m_next;
  526. }
  527. #if defined(_M_IX86) || defined(_M_X64)
  528. //
  529. // For conditional branch JE $LTarget, if both target and fallthrough branch has the same
  530. // instruction B, hoist it up and tail dup target branch:
  531. //
  532. // A <unconditional branch>
  533. // JE $LTarget $LTarget:
  534. // B B
  535. // ... JMP $L2
  536. //
  537. //====> hoist B up: move B up from fallthrough branch, remove B in target branch, retarget to $L2
  538. // $LTarget to $L2
  539. //
  540. // A <unconditional branch>
  541. // B $LTarget:
  542. // JE $L2 JMP $L2
  543. // ...
  544. //
  545. //====> now $LTarget becomes to be an empty BB, which can be removed if it's an unreferenced label
  546. //
  547. // A
  548. // B
  549. // JE $L2
  550. // ...
  551. //
  552. //====> Note B will be hoist above compare instruction A if there are no dependency between A and B
  553. //
  554. // B
  555. // A (cmp instr)
  556. // JE $L2
  557. // ...
  558. //
  559. IR::Instr *
  560. Peeps::HoistSameInstructionAboveSplit(IR::BranchInstr *branchInstr, IR::Instr *instrNext)
  561. {
  562. Assert(branchInstr);
  563. if (!branchInstr->IsConditional() || branchInstr->IsMultiBranch() || !branchInstr->IsLowered())
  564. {
  565. return instrNext; // this optimization only applies to single conditional branch
  566. }
  567. IR::LabelInstr *targetLabel = branchInstr->GetTarget();
  568. Assert(targetLabel);
  569. // give up if there are other branch entries to the target label
  570. if (targetLabel->labelRefs.Count() > 1)
  571. {
  572. return instrNext;
  573. }
  574. // Give up if previous instruction before target label has fallthrough, cannot hoist up
  575. IR::Instr *targetPrev = targetLabel->GetPrevRealInstrOrLabel();
  576. Assert(targetPrev);
  577. if (targetPrev->HasFallThrough())
  578. {
  579. return instrNext;
  580. }
  581. IR::Instr *instrSetCondition = NULL;
  582. IR::Instr *branchPrev = branchInstr->GetPrevRealInstrOrLabel();
  583. while (branchPrev && !branchPrev->StartsBasicBlock())
  584. {
  585. if (!instrSetCondition && EncoderMD::SetsConditionCode(branchPrev))
  586. { // located compare instruction for the branch
  587. instrSetCondition = branchPrev;
  588. }
  589. branchPrev = branchPrev->GetPrevRealInstrOrLabel(); // keep looking previous instr in BB
  590. }
  591. if (branchPrev && branchPrev->IsLabelInstr() && branchPrev->AsLabelInstr()->isOpHelper)
  592. { // don't apply the optimization when branch is in helper section
  593. return instrNext;
  594. }
  595. if (!instrSetCondition)
  596. { // give up if we cannot find the compare instruction in the BB, should be very rare
  597. return instrNext;
  598. }
  599. Assert(instrSetCondition);
  600. bool hoistAboveSetConditionInstr = false;
  601. if (instrSetCondition == branchInstr->GetPrevRealInstrOrLabel())
  602. { // if compare instruction is right before branch instruction, we can hoist above cmp instr
  603. hoistAboveSetConditionInstr = true;
  604. } // otherwise we hoist the identical instructions above conditional branch split only
  605. IR::Instr *instr = branchInstr->GetNextRealInstrOrLabel();
  606. IR::Instr *targetInstr = targetLabel->GetNextRealInstrOrLabel();
  607. IR::Instr *branchNextInstr = NULL;
  608. IR::Instr *targetNextInstr = NULL;
  609. IR::Instr *instrHasHoisted = NULL;
  610. Assert(instr && targetInstr);
  611. while (!instr->EndsBasicBlock() && !instr->IsLabelInstr() && instr->IsEqual(targetInstr) &&
  612. !EncoderMD::UsesConditionCode(instr) && !EncoderMD::SetsConditionCode(instr) &&
  613. !this->peepsAgen.DependentInstrs(instrSetCondition, instr) &&
  614. // cannot hoist InlineeStart from branch targets even for the same inlinee function.
  615. // it is used by encoder to generate InlineeFrameRecord for each inlinee
  616. instr->m_opcode != Js::OpCode::InlineeStart)
  617. {
  618. branchNextInstr = instr->GetNextRealInstrOrLabel();
  619. targetNextInstr = targetInstr->GetNextRealInstrOrLabel();
  620. instr->Unlink(); // hoist up instr in fallthrough branch
  621. if (hoistAboveSetConditionInstr)
  622. {
  623. instrSetCondition->InsertBefore(instr); // hoist above compare instruction
  624. }
  625. else
  626. {
  627. branchInstr->InsertBefore(instr); // hoist above branch split
  628. }
  629. targetInstr->Remove(); // remove the same instruction in target branch
  630. if (!instrHasHoisted)
  631. instrHasHoisted = instr; // points to the first hoisted instruction
  632. instr = branchNextInstr;
  633. targetInstr = targetNextInstr;
  634. Assert(instr && targetInstr);
  635. }
  636. if (instrHasHoisted)
  637. { // instructions have been hoisted, now check tail branch to see if it can be duplicated
  638. if (targetInstr->IsBranchInstr())
  639. {
  640. IR::BranchInstr *tailBranch = targetInstr->AsBranchInstr();
  641. if (tailBranch->IsUnconditional() && !tailBranch->IsMultiBranch())
  642. { // target can be replaced since tail branch is a single unconditional jmp
  643. branchInstr->ReplaceTarget(targetLabel, tailBranch->GetTarget());
  644. }
  645. // now targeLabel is an empty Basic Block, remove it if it's not referenced
  646. if (targetLabel->IsUnreferenced())
  647. {
  648. Peeps::PeepUnreachableLabel(targetLabel, targetLabel->isOpHelper);
  649. }
  650. }
  651. return instrHasHoisted;
  652. }
  653. return instrNext;
  654. }
  655. #endif
  656. IR::LabelInstr *
  657. Peeps::RetargetBrToBr(IR::BranchInstr *branchInstr, IR::LabelInstr * targetInstr)
  658. {
  659. AnalysisAssert(targetInstr);
  660. IR::Instr *targetInstrNext = targetInstr->GetNextRealInstr();
  661. AnalysisAssertMsg(targetInstrNext, "GetNextRealInstr() failed to get next target");
  662. // Removing branch to branch breaks some lexical assumptions about loop in sccliveness/linearscan/second chance.
  663. if (!branchInstr->IsLowered())
  664. {
  665. return targetInstr;
  666. }
  667. //
  668. // Retarget branch-to-branch
  669. //
  670. #if DBG
  671. uint counter = 0;
  672. #endif
  673. IR::LabelInstr *lastLoopTop = NULL;
  674. while (true)
  675. {
  676. // There's very few cases where we can safely follow a branch chain with intervening instrs.
  677. // One of them, which comes up occasionally, is where there is a copy of a single-def symbol
  678. // to another single-def symbol which is only used for the branch instruction (i.e. one dead
  679. // after the branch). Another is where a single-def symbol is declared of a constant (e.g. a
  680. // symbol created to store "True"
  681. // Unfortuantely, to properly do this, we'd need to do it somewhere else (i.e. not in peeps)
  682. // and make use of the additional information that we'd have there. Having the flow graph or
  683. // just any more information about variable liveness is necessary to determine that the load
  684. // instructions between jumps can be safely skipped.
  685. // The general case where this would be useful, on a higher level, is where a long statement
  686. // containing many branches returns a value; the branching here can put the result into some
  687. // different stacksym at each level, meaning that there'd be a load between each branch. The
  688. // result is that we don't currently optimize it.
  689. IR::BranchInstr *branchAtTarget = nullptr;
  690. if (targetInstrNext->IsBranchInstr())
  691. {
  692. branchAtTarget = targetInstrNext->AsBranchInstr();
  693. }
  694. else
  695. {
  696. // We don't have the information here to decide whether or not to continue the branch chain.
  697. break;
  698. }
  699. // This used to just be a targetInstrNext->AsBranchInstr()->IsUnconditional(), but, in order
  700. // to optimize further, it became necessary to handle more than just unconditional jumps. In
  701. // order to keep the code relatively clean, the "is it an inherently-taken jump chain" check
  702. // code now follows here:
  703. if (!targetInstrNext->AsBranchInstr()->IsUnconditional())
  704. {
  705. bool safetofollow = false;
  706. if(targetInstrNext->m_opcode == branchInstr->m_opcode)
  707. {
  708. // If it's the same branch instruction, with the same arguments, the branch decision should,
  709. // similarly, be the same. There's a bit more that can be done with this (e.g. for inverted,
  710. // but otherwise similar instructions like brTrue and brFalse, the destination could go down
  711. // the other path), but this is something that should probably be done more generally, so we
  712. // can optimize branch chains that have other interesting bechaviors.
  713. if (
  714. (
  715. (branchInstr->GetSrc1() && targetInstrNext->GetSrc1() && branchInstr->GetSrc1()->IsEqual(targetInstrNext->GetSrc1())) ||
  716. !(branchInstr->GetSrc1() || targetInstrNext->GetSrc1())
  717. ) && (
  718. (branchInstr->GetSrc2() && targetInstrNext->GetSrc2() && branchInstr->GetSrc2()->IsEqual(targetInstrNext->GetSrc2())) ||
  719. !(branchInstr->GetSrc2() || targetInstrNext->GetSrc2())
  720. )
  721. )
  722. {
  723. safetofollow = true;
  724. }
  725. }
  726. if (!safetofollow)
  727. {
  728. // We can't say safely that this branch is something that we can implicitly take, so instead
  729. // cut off the branch chain optimization here.
  730. break;
  731. }
  732. }
  733. // We don't want to skip the loop entry, unless we're right before the encoder
  734. if (targetInstr->m_isLoopTop && !branchAtTarget->IsLowered())
  735. {
  736. break;
  737. }
  738. if (targetInstr->m_isLoopTop)
  739. {
  740. if (targetInstr == lastLoopTop)
  741. {
  742. // We are back to a loopTop already visited.
  743. // Looks like an infinite loop somewhere...
  744. break;
  745. }
  746. lastLoopTop = targetInstr;
  747. }
  748. #if DBG
  749. if (!branchInstr->IsMultiBranch() && branchInstr->GetTarget()->m_noHelperAssert && !branchAtTarget->IsMultiBranch())
  750. {
  751. branchAtTarget->GetTarget()->m_noHelperAssert = true;
  752. }
  753. AssertMsg(++counter < 10000, "We should not be looping this many times!");
  754. #endif
  755. IR::LabelInstr * reTargetLabel = branchAtTarget->GetTarget();
  756. AnalysisAssert(reTargetLabel);
  757. if (targetInstr == reTargetLabel)
  758. {
  759. // Infinite loop.
  760. // JCC $L1
  761. // L1:
  762. // JMP $L1
  763. break;
  764. }
  765. if(branchInstr->IsMultiBranch())
  766. {
  767. IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
  768. multiBranchInstr->ChangeLabelRef(targetInstr, reTargetLabel);
  769. }
  770. else
  771. {
  772. branchInstr->SetTarget(reTargetLabel);
  773. }
  774. if (targetInstr->IsUnreferenced())
  775. {
  776. Peeps::PeepUnreachableLabel(targetInstr, false);
  777. }
  778. targetInstr = reTargetLabel;
  779. targetInstrNext = targetInstr->GetNextRealInstr();
  780. }
  781. return targetInstr;
  782. }
  783. IR::Instr *
  784. Peeps::PeepUnreachableLabel(IR::LabelInstr *deadLabel, const bool isInHelper, bool *const peepedRef)
  785. {
  786. Assert(deadLabel);
  787. Assert(deadLabel->IsUnreferenced());
  788. IR::Instr *prevFallthroughInstr = deadLabel;
  789. do
  790. {
  791. prevFallthroughInstr = prevFallthroughInstr->GetPrevRealInstrOrLabel();
  792. // The previous dead label may have been kept around due to a StatementBoundary, see comment in RemoveDeadBlock.
  793. } while(prevFallthroughInstr->IsLabelInstr() && prevFallthroughInstr->AsLabelInstr()->IsUnreferenced());
  794. IR::Instr *instrReturn;
  795. bool removeLabel;
  796. // If code is now unreachable, delete block
  797. if (!prevFallthroughInstr->HasFallThrough())
  798. {
  799. bool wasStatementBoundaryKeptInDeadBlock = false;
  800. instrReturn = RemoveDeadBlock(deadLabel->m_next, &wasStatementBoundaryKeptInDeadBlock);
  801. // Remove label only if we didn't have to keep last StatementBoundary in the dead block,
  802. // see comment in RemoveDeadBlock.
  803. removeLabel = !wasStatementBoundaryKeptInDeadBlock;
  804. if(peepedRef)
  805. {
  806. *peepedRef = true;
  807. }
  808. }
  809. else
  810. {
  811. instrReturn = deadLabel->m_next;
  812. removeLabel =
  813. deadLabel->isOpHelper == isInHelper
  814. #if DBG
  815. && !deadLabel->m_noHelperAssert
  816. #endif
  817. ;
  818. if(peepedRef)
  819. {
  820. *peepedRef = removeLabel;
  821. }
  822. }
  823. if (removeLabel && deadLabel->IsUnreferenced())
  824. {
  825. deadLabel->Remove();
  826. }
  827. return instrReturn;
  828. }
  829. IR::Instr *
  830. Peeps::CleanupLabel(IR::LabelInstr * instr, IR::LabelInstr * instrNext)
  831. {
  832. IR::Instr * returnInstr;
  833. IR::LabelInstr * labelToRemove;
  834. IR::LabelInstr * labelToKeep;
  835. // Just for dump, always keep loop top labels
  836. // We also can remove label that has non branch references
  837. if (instrNext->m_isLoopTop || instrNext->m_hasNonBranchRef)
  838. {
  839. if (instr->m_isLoopTop || instr->m_hasNonBranchRef)
  840. {
  841. // Don't remove loop top labels or labels with non branch references
  842. return instrNext;
  843. }
  844. labelToRemove = instr;
  845. labelToKeep = instrNext;
  846. returnInstr = instrNext;
  847. }
  848. else
  849. {
  850. labelToRemove = instrNext;
  851. labelToKeep = instr;
  852. returnInstr = instrNext->m_next;
  853. }
  854. while (!labelToRemove->labelRefs.Empty())
  855. {
  856. bool replaced = labelToRemove->labelRefs.Head()->ReplaceTarget(labelToRemove, labelToKeep);
  857. Assert(replaced);
  858. }
  859. if (labelToRemove->isOpHelper)
  860. {
  861. labelToKeep->isOpHelper = true;
  862. #if DBG
  863. if (labelToRemove->m_noHelperAssert)
  864. {
  865. labelToKeep->m_noHelperAssert = true;
  866. }
  867. #endif
  868. }
  869. labelToRemove->Remove();
  870. return returnInstr;
  871. }
  872. //
  873. // Removes instrs starting from one specified by the 'instr' parameter.
  874. // Keeps last statement boundary in the whole block to remove.
  875. // Stops at label or exit instr.
  876. // Return value:
  877. // - 1st instr that is label or end instr, except the case when forceRemoveFirstInstr is true, in which case
  878. // we start checking for exit loop condition from next instr.
  879. // Notes:
  880. // - if wasStmtBoundaryKeptInDeadBlock is not NULL, it receives true when we didn't remove last
  881. // StatementBoundary pragma instr as otherwise it would be non-helper/helper move of the pragma instr.
  882. // If there was no stmt boundary or last stmt boundary moved to after next label, that would receive false.
  883. //
  884. IR::Instr *Peeps::RemoveDeadBlock(IR::Instr *instr, bool* wasStmtBoundaryKeptInDeadBlock /* = nullptr */)
  885. {
  886. IR::Instr* lastStatementBoundary = nullptr;
  887. while (instr && !instr->IsLabelInstr() && !instr->IsExitInstr())
  888. {
  889. IR::Instr *deadInstr = instr;
  890. instr = instr->m_next;
  891. if (deadInstr->IsPragmaInstr() && deadInstr->m_opcode == Js::OpCode::StatementBoundary)
  892. {
  893. if (lastStatementBoundary)
  894. {
  895. //Its enough if we keep latest statement boundary. Rest are dead anyway.
  896. lastStatementBoundary->Remove();
  897. }
  898. lastStatementBoundary = deadInstr;
  899. }
  900. else
  901. {
  902. deadInstr->Remove();
  903. }
  904. }
  905. // Do not let StatementBoundary to move across non-helper and helper blocks, very important under debugger:
  906. // if we let that happen, helper block can be moved to the end of the func so that statement maps will miss one statement.
  907. // Issues can be when (normally, StatementBoundary should never belong to a helper label):
  908. // - if we remove the label and prev label is a helper, StatementBoundary will be moved inside helper.
  909. // - if we move StatementBoundary under next label which is a helper, same problem again.
  910. bool canMoveStatementBoundaryUnderNextLabel = instr && instr->IsLabelInstr() && !instr->AsLabelInstr()->isOpHelper;
  911. if (lastStatementBoundary && canMoveStatementBoundaryUnderNextLabel)
  912. {
  913. lastStatementBoundary->Unlink();
  914. instr->InsertAfter(lastStatementBoundary);
  915. }
  916. if (wasStmtBoundaryKeptInDeadBlock)
  917. {
  918. *wasStmtBoundaryKeptInDeadBlock = lastStatementBoundary && !canMoveStatementBoundaryUnderNextLabel;
  919. }
  920. return instr;
  921. }
  922. // Shared code for x86 and amd64
  923. #if defined(_M_IX86) || defined(_M_X64)
  924. IR::Instr *
  925. Peeps::PeepRedundant(IR::Instr *instr)
  926. {
  927. IR::Instr *retInstr = instr;
  928. if (instr->m_opcode == Js::OpCode::ADD || instr->m_opcode == Js::OpCode::SUB || instr->m_opcode == Js::OpCode::OR)
  929. {
  930. Assert(instr->GetSrc1() && instr->GetSrc2());
  931. if( (instr->GetSrc2()->IsIntConstOpnd() && instr->GetSrc2()->AsIntConstOpnd()->GetValue() == 0))
  932. {
  933. // remove instruction
  934. retInstr = instr->m_next;
  935. instr->Remove();
  936. }
  937. }
  938. #if _M_IX86
  939. RegNum edx = RegEDX;
  940. #else
  941. RegNum edx = RegRDX;
  942. #endif
  943. if (instr->m_opcode == Js::OpCode::NOP && instr->GetDst() != NULL
  944. && instr->GetDst()->IsRegOpnd() && instr->GetDst()->AsRegOpnd()->GetReg() == edx)
  945. {
  946. // dummy def used for non-32bit ovf check for IMUL
  947. // check edx is not killed between IMUL and edx = NOP, then remove the NOP
  948. bool found = false;
  949. IR::Instr *nopInstr = instr;
  950. do
  951. {
  952. instr = instr->GetPrevRealInstrOrLabel();
  953. if (
  954. instr->m_opcode == Js::OpCode::IMUL ||
  955. (instr->m_opcode == Js::OpCode::CALL && this->func->GetJITFunctionBody()->IsWasmFunction())
  956. )
  957. {
  958. found = true;
  959. break;
  960. }
  961. } while(!instr->StartsBasicBlock());
  962. if (found)
  963. {
  964. retInstr = nopInstr->m_next;
  965. nopInstr->Remove();
  966. }
  967. else
  968. {
  969. instr = nopInstr;
  970. do
  971. {
  972. instr = instr->GetNextRealInstrOrLabel();
  973. if (instr->m_opcode == Js::OpCode::DIV)
  974. {
  975. found = true;
  976. retInstr = nopInstr->m_next;
  977. nopInstr->Remove();
  978. break;
  979. }
  980. } while (!instr->EndsBasicBlock());
  981. AssertMsg(found, "edx = NOP without an IMUL or DIV");
  982. }
  983. }
  984. return retInstr;
  985. }
  986. /*
  987. Work backwards from the label instruction to look for this pattern:
  988. Jcc $Label
  989. Mov
  990. Mov
  991. ..
  992. Mov
  993. Label:
  994. If found, we remove the Jcc, convert MOVs to CMOVcc and remove the label if unreachable.
  995. */
  996. IR::Instr*
  997. Peeps::PeepCondMove(IR::LabelInstr *labelInstr, IR::Instr *nextInstr, const bool isInHelper)
  998. {
  999. IR::Instr *instr = labelInstr->GetPrevRealInstrOrLabel();
  1000. Js::OpCode newOpCode = Js::OpCode::InvalidOpCode;
  1001. // Check if BB is all MOVs with both RegOpnd
  1002. while(instr->m_opcode == Js::OpCode::MOV)
  1003. {
  1004. if (!instr->GetSrc1()->IsRegOpnd() || !instr->GetDst()->IsRegOpnd())
  1005. return nextInstr;
  1006. instr = instr->GetPrevRealInstrOrLabel();
  1007. }
  1008. // Did we hit a conditional branch ?
  1009. if (instr->IsBranchInstr() && instr->AsBranchInstr()->IsConditional() &&
  1010. !instr->AsBranchInstr()->IsMultiBranch() &&
  1011. instr->AsBranchInstr()->GetTarget() == labelInstr &&
  1012. instr->m_opcode != Js::OpCode::Leave)
  1013. {
  1014. IR::BranchInstr *brInstr = instr->AsBranchInstr();
  1015. // Get the correct CMOVcc
  1016. switch(brInstr->m_opcode)
  1017. {
  1018. case Js::OpCode::JA:
  1019. newOpCode = Js::OpCode::CMOVBE;
  1020. break;
  1021. case Js::OpCode::JAE:
  1022. newOpCode = Js::OpCode::CMOVB;
  1023. break;
  1024. case Js::OpCode::JB:
  1025. newOpCode = Js::OpCode::CMOVAE;
  1026. break;
  1027. case Js::OpCode::JBE:
  1028. newOpCode = Js::OpCode::CMOVA;
  1029. break;
  1030. case Js::OpCode::JEQ:
  1031. newOpCode = Js::OpCode::CMOVNE;
  1032. break;
  1033. case Js::OpCode::JNE:
  1034. newOpCode = Js::OpCode::CMOVE;
  1035. break;
  1036. case Js::OpCode::JNP:
  1037. newOpCode = Js::OpCode::CMOVP;
  1038. break;
  1039. case Js::OpCode::JLT:
  1040. newOpCode = Js::OpCode::CMOVGE;
  1041. break;
  1042. case Js::OpCode::JLE:
  1043. newOpCode = Js::OpCode::CMOVG;
  1044. break;
  1045. case Js::OpCode::JGT:
  1046. newOpCode = Js::OpCode::CMOVLE;
  1047. break;
  1048. case Js::OpCode::JGE:
  1049. newOpCode = Js::OpCode::CMOVL;
  1050. break;
  1051. case Js::OpCode::JNO:
  1052. newOpCode = Js::OpCode::CMOVO;
  1053. break;
  1054. case Js::OpCode::JO:
  1055. newOpCode = Js::OpCode::CMOVNO;
  1056. break;
  1057. case Js::OpCode::JP:
  1058. newOpCode = Js::OpCode::CMOVNP;
  1059. break;
  1060. case Js::OpCode::JNSB:
  1061. newOpCode = Js::OpCode::CMOVS;
  1062. break;
  1063. case Js::OpCode::JSB:
  1064. newOpCode = Js::OpCode::CMOVNS;
  1065. break;
  1066. default:
  1067. Assert(UNREACHED);
  1068. __assume(UNREACHED);
  1069. }
  1070. // convert the entire block to CMOVs
  1071. instr = brInstr->GetNextRealInstrOrLabel();
  1072. while(instr != labelInstr)
  1073. {
  1074. instr->m_opcode = newOpCode;
  1075. instr = instr->GetNextRealInstrOrLabel();
  1076. }
  1077. // remove the Jcc
  1078. brInstr->Remove();
  1079. // We may have exposed an unreachable label by deleting the branch
  1080. if (labelInstr->IsUnreferenced())
  1081. return Peeps::PeepUnreachableLabel(labelInstr, isInHelper);
  1082. }
  1083. return nextInstr;
  1084. }
  1085. #endif