Peeps.cpp 42 KB

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