SccLiveness.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. #include "SccLiveness.h"
  7. // Build SCC liveness. SCC stands for Strongly Connected Components. It's a simple
  8. // conservative algorithm which has the advantage of being O(N). A simple forward walk
  9. // of the IR looks at the first and last use of each symbols and creates the lifetimes.
  10. // The code assumes the blocks are in R-DFO order to start with. For loops, the lifetimes
  11. // are simply extended to cover the whole loop.
  12. //
  13. // The disadvantages are:
  14. // o Separate lifetimes of a given symbol are not separated
  15. // o Very conservative in loops, which is where we'd like precise info...
  16. //
  17. // Single-def symbols do not have the first issue. We also try to make up for number 2
  18. // by not extending the lifetime of symbols if the first def and the last use are in
  19. // same loop.
  20. //
  21. // The code builds a list of lifetimes sorted in start order.
  22. // We actually build the list in reverse start order, and then reverse it.
  23. void
  24. SCCLiveness::Build()
  25. {
  26. // First, lets number each instruction to get an ordering.
  27. // Note that we assume the blocks are in RDFO.
  28. // NOTE: Currently the DoInterruptProbe pass will number the instructions. If it has,
  29. // then the numbering here is not necessary. But there should be no phase between the two
  30. // that can invalidate the numbering.
  31. if (!this->func->HasInstrNumber())
  32. {
  33. this->func->NumberInstrs();
  34. }
  35. IR::LabelInstr *lastLabelInstr = nullptr;
  36. FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func)
  37. {
  38. IR::Opnd *dst, *src1, *src2;
  39. uint32 instrNum = instr->GetNumber();
  40. if (instr->HasBailOutInfo())
  41. {
  42. // At this point, the bailout should be lowered to a CALL to BailOut
  43. #if DBG
  44. Assert(LowererMD::IsCall(instr));
  45. IR::Opnd * helperOpnd = nullptr;
  46. if (instr->GetSrc1()->IsHelperCallOpnd())
  47. {
  48. helperOpnd = instr->GetSrc1();
  49. }
  50. else if (instr->GetSrc1()->AsRegOpnd()->m_sym)
  51. {
  52. Assert(instr->GetSrc1()->AsRegOpnd()->m_sym->m_instrDef);
  53. helperOpnd = instr->GetSrc1()->AsRegOpnd()->m_sym->m_instrDef->GetSrc1();
  54. }
  55. Assert(!helperOpnd || BailOutInfo::IsBailOutHelper(helperOpnd->AsHelperCallOpnd()->m_fnHelper));
  56. #endif
  57. ProcessBailOutUses(instr);
  58. }
  59. if (instr->m_opcode == Js::OpCode::InlineeEnd && instr->m_func->m_hasInlineArgsOpt)
  60. {
  61. instr->m_func->frameInfo->IterateSyms([=](StackSym* argSym)
  62. {
  63. this->ProcessStackSymUse(argSym, instr);
  64. });
  65. }
  66. // Process srcs
  67. src1 = instr->GetSrc1();
  68. if (src1)
  69. {
  70. this->ProcessSrc(src1, instr);
  71. src2 = instr->GetSrc2();
  72. if (src2)
  73. {
  74. this->ProcessSrc(src2, instr);
  75. }
  76. }
  77. // End of loop?
  78. if (this->curLoop && instrNum >= this->curLoop->regAlloc.loopEnd)
  79. {
  80. AssertMsg(this->loopNest > 0, "Loop nest is messed up");
  81. AssertMsg(instr->IsBranchInstr(), "Loop tail should be a branchInstr");
  82. AssertMsg(instr->AsBranchInstr()->IsLoopTail(this->func), "Loop tail not marked correctly");
  83. Loop *loop = this->curLoop;
  84. while (loop && loop->regAlloc.loopEnd == this->curLoop->regAlloc.loopEnd)
  85. {
  86. FOREACH_SLIST_ENTRY(Lifetime *, lifetime, loop->regAlloc.extendedLifetime)
  87. {
  88. if (loop->regAlloc.hasNonOpHelperCall)
  89. {
  90. lifetime->isLiveAcrossUserCalls = true;
  91. }
  92. if (loop->regAlloc.hasCall)
  93. {
  94. lifetime->isLiveAcrossCalls = true;
  95. }
  96. if (lifetime->end == loop->regAlloc.loopEnd)
  97. {
  98. lifetime->totalOpHelperLengthByEnd = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(instr);
  99. }
  100. }
  101. NEXT_SLIST_ENTRY;
  102. loop->regAlloc.helperLength = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(instr);
  103. Assert(!loop->parent || loop->parent && loop->parent->regAlloc.loopEnd >= loop->regAlloc.loopEnd);
  104. loop = loop->parent;
  105. }
  106. while (this->curLoop && instrNum >= this->curLoop->regAlloc.loopEnd)
  107. {
  108. this->curLoop = this->curLoop->parent;
  109. this->loopNest--;
  110. }
  111. }
  112. // Keep track of the last call instruction number to find out whether a lifetime crosses a call
  113. // Do not count call to bailout which exits anyways
  114. if (LowererMD::IsCall(instr) && !instr->HasBailOutInfo())
  115. {
  116. if (this->lastOpHelperLabel == nullptr)
  117. {
  118. // Catch only user calls (non op helper calls)
  119. this->lastNonOpHelperCall = instr->GetNumber();
  120. if (this->curLoop)
  121. {
  122. this->curLoop->regAlloc.hasNonOpHelperCall = true;
  123. }
  124. }
  125. // Catch all calls
  126. this->lastCall = instr->GetNumber();
  127. if (this->curLoop)
  128. {
  129. this->curLoop->regAlloc.hasCall = true;
  130. }
  131. }
  132. // Process dst
  133. dst = instr->GetDst();
  134. if (dst)
  135. {
  136. this->ProcessDst(dst, instr);
  137. }
  138. if (instr->IsLabelInstr())
  139. {
  140. IR::LabelInstr * const labelInstr = instr->AsLabelInstr();
  141. if (labelInstr->IsUnreferenced())
  142. {
  143. // Unreferenced labels can potentially be removed. See if the label tells
  144. // us we're transitioning between a helper and non-helper block.
  145. if (labelInstr->isOpHelper == (this->lastOpHelperLabel != nullptr)
  146. && lastLabelInstr && labelInstr->isOpHelper == lastLabelInstr->isOpHelper)
  147. {
  148. labelInstr->Remove();
  149. continue;
  150. }
  151. }
  152. lastLabelInstr = labelInstr;
  153. Region * region = labelInstr->GetRegion();
  154. if (region != nullptr)
  155. {
  156. if (this->curRegion && this->curRegion != region)
  157. {
  158. this->curRegion->SetEnd(labelInstr->m_prev);
  159. }
  160. if (region->GetStart() == nullptr)
  161. {
  162. region->SetStart(labelInstr);
  163. }
  164. region->SetEnd(nullptr);
  165. this->curRegion = region;
  166. }
  167. else
  168. {
  169. labelInstr->SetRegion(this->curRegion);
  170. }
  171. // Look for start of loop
  172. if (labelInstr->m_isLoopTop)
  173. {
  174. this->loopNest++; // used in spill cost calculation.
  175. uint32 lastBranchNum = 0;
  176. IR::BranchInstr *lastBranchInstr = nullptr;
  177. FOREACH_SLISTCOUNTED_ENTRY(IR::BranchInstr *, ref, &labelInstr->labelRefs)
  178. {
  179. if (ref->GetNumber() > lastBranchNum)
  180. {
  181. lastBranchInstr = ref;
  182. lastBranchNum = lastBranchInstr->GetNumber();
  183. }
  184. }
  185. NEXT_SLISTCOUNTED_ENTRY;
  186. AssertMsg(instrNum < lastBranchNum, "Didn't find back edge...");
  187. AssertMsg(lastBranchInstr->IsLoopTail(this->func), "Loop tail not marked properly");
  188. Loop * loop = labelInstr->GetLoop();
  189. loop->parent = this->curLoop;
  190. this->curLoop = loop;
  191. loop->regAlloc.loopStart = instrNum;
  192. loop->regAlloc.loopEnd = lastBranchNum;
  193. #if LOWER_SPLIT_INT64
  194. func->Int64SplitExtendLoopLifetime(loop);
  195. #endif
  196. // Tail duplication can result in cases in which an outer loop lexically ends before the inner loop.
  197. // The register allocator could then thrash in the inner loop registers used for a live-on-back-edge
  198. // sym on the outer loop. To prevent this, we need to mark the end of the outer loop as the end of the
  199. // inner loop and update the lifetimes already extended in the outer loop in keeping with this change.
  200. for (Loop* parentLoop = loop->parent; parentLoop != nullptr; parentLoop = parentLoop->parent)
  201. {
  202. if (parentLoop->regAlloc.loopEnd < loop->regAlloc.loopEnd)
  203. {
  204. // We need to go over extended lifetimes in outer loops to update the lifetimes of symbols that might
  205. // have had their lifetime extended to the outer loop end (which is before the current loop end) and
  206. // may not have any uses in the current loop to extend their lifetimes to the current loop end.
  207. FOREACH_SLIST_ENTRY(Lifetime *, lifetime, parentLoop->regAlloc.extendedLifetime)
  208. {
  209. if (lifetime->end == parentLoop->regAlloc.loopEnd)
  210. {
  211. lifetime->end = loop->regAlloc.loopEnd;
  212. }
  213. }
  214. NEXT_SLIST_ENTRY;
  215. parentLoop->regAlloc.loopEnd = loop->regAlloc.loopEnd;
  216. }
  217. }
  218. loop->regAlloc.extendedLifetime = JitAnew(this->tempAlloc, SList<Lifetime *>, this->tempAlloc);
  219. loop->regAlloc.hasNonOpHelperCall = false;
  220. loop->regAlloc.hasCall = false;
  221. loop->regAlloc.hasAirLock = false;
  222. }
  223. // track whether we are in a helper block or not
  224. if (this->lastOpHelperLabel != nullptr)
  225. {
  226. this->EndOpHelper(labelInstr);
  227. }
  228. if (labelInstr->isOpHelper && !PHASE_OFF(Js::OpHelperRegOptPhase, this->func))
  229. {
  230. this->lastOpHelperLabel = labelInstr;
  231. }
  232. }
  233. else if (instr->IsBranchInstr() && !instr->AsBranchInstr()->IsMultiBranch())
  234. {
  235. IR::LabelInstr * branchTarget = instr->AsBranchInstr()->GetTarget();
  236. Js::OpCode brOpcode = instr->m_opcode;
  237. if (branchTarget->GetRegion() == nullptr && this->func->HasTry())
  238. {
  239. Assert(brOpcode != Js::OpCode::Leave && brOpcode != Js::OpCode::TryCatch && brOpcode != Js::OpCode::TryFinally);
  240. branchTarget->SetRegion(this->curRegion);
  241. }
  242. }
  243. if (this->lastOpHelperLabel != nullptr && instr->IsBranchInstr())
  244. {
  245. IR::LabelInstr *targetLabel = instr->AsBranchInstr()->GetTarget();
  246. if (targetLabel->isOpHelper && instr->AsBranchInstr()->IsConditional())
  247. {
  248. // If we have:
  249. // L1: [helper]
  250. // CMP
  251. // JCC helperLabel
  252. // code
  253. // Insert a helper label before 'code' to mark this is also helper code.
  254. IR::Instr *branchInstrNext = instr->GetNextRealInstrOrLabel();
  255. if (!branchInstrNext->IsLabelInstr())
  256. {
  257. instrNext = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, true);
  258. instr->InsertAfter(instrNext);
  259. instrNext->CopyNumber(instrNext->m_next);
  260. }
  261. }
  262. this->EndOpHelper(instr);
  263. }
  264. }NEXT_INSTR_IN_FUNC_EDITING;
  265. if (this->func->HasTry())
  266. {
  267. #if DBG
  268. FOREACH_INSTR_IN_FUNC(instr, this->func)
  269. {
  270. if (instr->IsLabelInstr())
  271. {
  272. Assert(instr->AsLabelInstr()->GetRegion() != nullptr);
  273. }
  274. }
  275. NEXT_INSTR_IN_FUNC
  276. #endif
  277. AssertMsg(this->curRegion, "Function with try but no regions?");
  278. AssertMsg(this->curRegion->GetStart() && !this->curRegion->GetEnd(), "Current region not active?");
  279. // Check for lifetimes that have been extended such that they now span multiple regions.
  280. this->curRegion->SetEnd(this->func->m_exitInstr);
  281. if (this->func->HasTry() && !this->func->DoOptimizeTry())
  282. {
  283. FOREACH_SLIST_ENTRY(Lifetime *, lifetime, &this->lifetimeList)
  284. {
  285. if (lifetime->dontAllocate)
  286. {
  287. continue;
  288. }
  289. if (lifetime->start < lifetime->region->GetStart()->GetNumber() ||
  290. lifetime->end > lifetime->region->GetEnd()->GetNumber())
  291. {
  292. lifetime->dontAllocate = true;
  293. }
  294. }
  295. NEXT_SLIST_ENTRY;
  296. }
  297. }
  298. AssertMsg(this->loopNest == 0, "LoopNest is messed up");
  299. // The list is built in reverse order. Let's flip it in increasing start order.
  300. this->lifetimeList.Reverse();
  301. this->opHelperBlockList.Reverse();
  302. #if DBG_DUMP
  303. if (PHASE_DUMP(Js::LivenessPhase, this->func))
  304. {
  305. this->Dump();
  306. }
  307. #endif
  308. }
  309. void
  310. SCCLiveness::EndOpHelper(IR::Instr * instr)
  311. {
  312. Assert(this->lastOpHelperLabel != nullptr);
  313. OpHelperBlock * opHelperBlock = this->opHelperBlockList.PrependNode(this->tempAlloc);
  314. Assert(opHelperBlock != nullptr);
  315. opHelperBlock->opHelperLabel = this->lastOpHelperLabel;
  316. opHelperBlock->opHelperEndInstr = instr;
  317. this->totalOpHelperFullVisitedLength += opHelperBlock->Length();
  318. this->lastOpHelperLabel = nullptr;
  319. }
  320. // SCCLiveness::ProcessSrc
  321. void
  322. SCCLiveness::ProcessSrc(IR::Opnd *src, IR::Instr *instr)
  323. {
  324. if (src->IsRegOpnd())
  325. {
  326. this->ProcessRegUse(src->AsRegOpnd(), instr);
  327. }
  328. else if (src->IsIndirOpnd())
  329. {
  330. IR::IndirOpnd *indirOpnd = src->AsIndirOpnd();
  331. if (!this->FoldIndir(instr, indirOpnd))
  332. {
  333. if (indirOpnd->GetBaseOpnd())
  334. {
  335. this->ProcessRegUse(indirOpnd->GetBaseOpnd(), instr);
  336. }
  337. if (indirOpnd->GetIndexOpnd())
  338. {
  339. this->ProcessRegUse(indirOpnd->GetIndexOpnd(), instr);
  340. }
  341. }
  342. }
  343. else if (src->IsListOpnd())
  344. {
  345. src->AsListOpnd()->Map([&](int i, IR::Opnd* opnd) { this->ProcessSrc(opnd, instr); });
  346. }
  347. else if (!this->lastCall && src->IsSymOpnd() && src->AsSymOpnd()->m_sym->AsStackSym()->IsParamSlotSym())
  348. {
  349. IR::SymOpnd *symOpnd = src->AsSymOpnd();
  350. RegNum reg = LinearScanMD::GetParamReg(symOpnd, this->func);
  351. if (reg != RegNOREG && PHASE_ON(Js::RegParamsPhase, this->func))
  352. {
  353. StackSym *stackSym = symOpnd->m_sym->AsStackSym();
  354. Lifetime *lifetime = stackSym->scratch.linearScan.lifetime;
  355. if (lifetime == nullptr)
  356. {
  357. lifetime = this->InsertLifetime(stackSym, reg, this->func->m_headInstr->m_next);
  358. lifetime->region = this->curRegion;
  359. lifetime->isFloat = symOpnd->IsFloat() || symOpnd->IsSimd128();
  360. }
  361. IR::RegOpnd * newRegOpnd = IR::RegOpnd::New(stackSym, reg, symOpnd->GetType(), this->func);
  362. instr->ReplaceSrc(symOpnd, newRegOpnd);
  363. this->ProcessRegUse(newRegOpnd, instr);
  364. }
  365. }
  366. }
  367. // SCCLiveness::ProcessDst
  368. void
  369. SCCLiveness::ProcessDst(IR::Opnd *dst, IR::Instr *instr)
  370. {
  371. if (dst->IsIndirOpnd())
  372. {
  373. // Indir regs are really uses
  374. IR::IndirOpnd *indirOpnd = dst->AsIndirOpnd();
  375. if (!this->FoldIndir(instr, indirOpnd))
  376. {
  377. if (indirOpnd->GetBaseOpnd())
  378. {
  379. this->ProcessRegUse(indirOpnd->GetBaseOpnd(), instr);
  380. }
  381. if (indirOpnd->GetIndexOpnd())
  382. {
  383. this->ProcessRegUse(indirOpnd->GetIndexOpnd(), instr);
  384. }
  385. }
  386. }
  387. #if defined(_M_X64) || defined(_M_IX86)
  388. else if (instr->m_opcode == Js::OpCode::SHUFPS || instr->m_opcode == Js::OpCode::SHUFPD)
  389. {
  390. // dst is the first src, make sure it gets the same live reg
  391. this->ProcessRegUse(dst->AsRegOpnd(), instr);
  392. }
  393. #endif
  394. else if (dst->IsRegOpnd())
  395. {
  396. this->ProcessRegDef(dst->AsRegOpnd(), instr);
  397. }
  398. else if (dst->IsListOpnd())
  399. {
  400. dst->AsListOpnd()->Map([&](int i, IR::Opnd* opnd) { this->ProcessDst(opnd, instr); });
  401. }
  402. }
  403. void
  404. SCCLiveness::ProcessBailOutUses(IR::Instr * instr)
  405. {
  406. BailOutInfo * bailOutInfo = instr->GetBailOutInfo();
  407. FOREACH_BITSET_IN_SPARSEBV(id, bailOutInfo->byteCodeUpwardExposedUsed)
  408. {
  409. StackSym * stackSym = this->func->m_symTable->FindStackSym(id);
  410. Assert(stackSym != nullptr);
  411. ProcessStackSymUse(stackSym, instr);
  412. }
  413. NEXT_BITSET_IN_SPARSEBV;
  414. FOREACH_SLISTBASE_ENTRY(CopyPropSyms, copyPropSyms, &bailOutInfo->usedCapturedValues.copyPropSyms)
  415. {
  416. ProcessStackSymUse(copyPropSyms.Value(), instr);
  417. }
  418. NEXT_SLISTBASE_ENTRY;
  419. bailOutInfo->IterateArgOutSyms([=] (uint, uint, StackSym* sym) {
  420. if(!sym->IsArgSlotSym() && sym->m_isBailOutReferenced)
  421. {
  422. ProcessStackSymUse(sym, instr);
  423. }
  424. });
  425. if(bailOutInfo->branchConditionOpnd)
  426. {
  427. ProcessSrc(bailOutInfo->branchConditionOpnd, instr);
  428. }
  429. // BailOnNoProfile might have caused the deletion of a cloned InlineeEnd. As a result, argument
  430. // lifetimes wouldn't have been extended beyond the bailout point (InlineeEnd extends the lifetimes)
  431. // Extend argument lifetimes up to the bail out point to allow LinearScan::SpillInlineeArgs to spill
  432. // inlinee args.
  433. if (instr->HasBailOnNoProfile() && !instr->m_func->IsTopFunc())
  434. {
  435. Func * inlinee = instr->m_func;
  436. while (!inlinee->IsTopFunc())
  437. {
  438. if (inlinee->m_hasInlineArgsOpt && inlinee->frameInfo->isRecorded)
  439. {
  440. inlinee->frameInfo->IterateSyms([=](StackSym* argSym)
  441. {
  442. this->ProcessStackSymUse(argSym, instr);
  443. });
  444. inlinee = inlinee->GetParentFunc();
  445. }
  446. else
  447. {
  448. // if an inlinee's arguments haven't been optimized away, it's ancestors' shouldn't have been too.
  449. break;
  450. }
  451. }
  452. }
  453. }
  454. void
  455. SCCLiveness::ProcessStackSymUse(StackSym * stackSym, IR::Instr * instr, int usageSize)
  456. {
  457. Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
  458. if (lifetime == nullptr)
  459. {
  460. #if DBG
  461. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  462. Output::Print(_u("Function: %s (%s) "), this->func->GetJITFunctionBody()->GetDisplayName(), this->func->GetDebugNumberSet(debugStringBuffer));
  463. Output::Print(_u("Reg: "));
  464. stackSym->Dump();
  465. Output::Print(_u("\n"));
  466. Output::Flush();
  467. #endif
  468. AnalysisAssertMsg(UNREACHED, "Uninitialized reg?");
  469. }
  470. else
  471. {
  472. if (lifetime->region != this->curRegion && !this->func->DoOptimizeTry())
  473. {
  474. lifetime->dontAllocate = true;
  475. }
  476. ExtendLifetime(lifetime, instr);
  477. }
  478. lifetime->AddToUseCount(LinearScan::GetUseSpillCost(this->loopNest, (this->lastOpHelperLabel != nullptr)), this->curLoop, this->func);
  479. if (lifetime->start < this->lastCall)
  480. {
  481. lifetime->isLiveAcrossCalls = true;
  482. }
  483. if (lifetime->start < this->lastNonOpHelperCall)
  484. {
  485. lifetime->isLiveAcrossUserCalls = true;
  486. }
  487. lifetime->isDeadStore = false;
  488. lifetime->intUsageBv.Set(usageSize);
  489. }
  490. // SCCLiveness::ProcessRegUse
  491. void
  492. SCCLiveness::ProcessRegUse(IR::RegOpnd *regUse, IR::Instr *instr)
  493. {
  494. StackSym * stackSym = regUse->m_sym;
  495. if (stackSym == nullptr)
  496. {
  497. return;
  498. }
  499. ProcessStackSymUse(stackSym, instr, TySize[regUse->GetType()]);
  500. }
  501. // SCCLiveness::ProcessRegDef
  502. void
  503. SCCLiveness::ProcessRegDef(IR::RegOpnd *regDef, IR::Instr *instr)
  504. {
  505. StackSym * stackSym = regDef->m_sym;
  506. // PhysReg
  507. if (stackSym == nullptr || regDef->GetReg() != RegNOREG)
  508. {
  509. IR::Opnd *src = instr->GetSrc1();
  510. // If this symbol is assigned to a physical register, let's tell the register
  511. // allocator to prefer assigning that register to the lifetime.
  512. //
  513. // Note: this only pays off if this is the last-use of the symbol, but
  514. // unfortunately we don't have a way to tell that currently...
  515. if (LowererMD::IsAssign(instr) && src->IsRegOpnd() && src->AsRegOpnd()->m_sym)
  516. {
  517. StackSym *srcSym = src->AsRegOpnd()->m_sym;
  518. srcSym->scratch.linearScan.lifetime->regPreference.Set(regDef->GetReg());
  519. }
  520. // This physreg doesn't have a lifetime, just return.
  521. if (stackSym == nullptr)
  522. {
  523. return;
  524. }
  525. }
  526. // Arg slot sym can be in a RegOpnd for param passed via registers
  527. // Skip creating a lifetime for those.
  528. if (stackSym->IsArgSlotSym())
  529. {
  530. return;
  531. }
  532. // We'll extend the lifetime only if there are uses in a different loop region
  533. // from one of the defs.
  534. Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
  535. if (lifetime == nullptr)
  536. {
  537. lifetime = this->InsertLifetime(stackSym, regDef->GetReg(), instr);
  538. lifetime->region = this->curRegion;
  539. lifetime->isFloat = regDef->IsFloat() || regDef->IsSimd128();
  540. }
  541. else
  542. {
  543. AssertMsg(lifetime->start <= instr->GetNumber(), "Lifetime start not set correctly");
  544. ExtendLifetime(lifetime, instr);
  545. if (lifetime->region != this->curRegion && !this->func->DoOptimizeTry())
  546. {
  547. lifetime->dontAllocate = true;
  548. }
  549. }
  550. lifetime->AddToUseCount(LinearScan::GetUseSpillCost(this->loopNest, (this->lastOpHelperLabel != nullptr)), this->curLoop, this->func);
  551. lifetime->intUsageBv.Set(TySize[regDef->GetType()]);
  552. }
  553. // SCCLiveness::ExtendLifetime
  554. // Manages extend lifetimes to the end of loops if the corresponding symbol
  555. // is live on the back edge of the loop
  556. void
  557. SCCLiveness::ExtendLifetime(Lifetime *lifetime, IR::Instr *instr)
  558. {
  559. AssertMsg(lifetime != nullptr, "Lifetime not provided");
  560. AssertMsg(lifetime->sym != nullptr, "Lifetime has no symbol");
  561. Assert(this->extendedLifetimesLoopList->Empty());
  562. // Find the loop that we need to extend the lifetime to
  563. StackSym * sym = lifetime->sym;
  564. Loop * loop = this->curLoop;
  565. uint32 extendedLifetimeStart = lifetime->start;
  566. uint32 extendedLifetimeEnd = lifetime->end;
  567. bool isLiveOnBackEdge = false;
  568. bool loopAddedToList = false;
  569. while (loop)
  570. {
  571. if (loop->regAlloc.liveOnBackEdgeSyms->Test(sym->m_id))
  572. {
  573. isLiveOnBackEdge = true;
  574. if (loop->regAlloc.loopStart < extendedLifetimeStart)
  575. {
  576. extendedLifetimeStart = loop->regAlloc.loopStart;
  577. this->extendedLifetimesLoopList->Prepend(this->tempAlloc, loop);
  578. loopAddedToList = true;
  579. }
  580. if (loop->regAlloc.loopEnd > extendedLifetimeEnd)
  581. {
  582. extendedLifetimeEnd = loop->regAlloc.loopEnd;
  583. if (!loopAddedToList)
  584. {
  585. this->extendedLifetimesLoopList->Prepend(this->tempAlloc, loop);
  586. }
  587. }
  588. }
  589. loop = loop->parent;
  590. loopAddedToList = false;
  591. }
  592. if (!isLiveOnBackEdge)
  593. {
  594. // Don't extend lifetime to loop boundary if the use are not live on back edge
  595. // Note: the above loop doesn't detect a reg that is live on an outer back edge
  596. // but not an inner one, so we can't assume here that the lifetime hasn't been extended
  597. // past the current instruction.
  598. if (lifetime->end < instr->GetNumber())
  599. {
  600. lifetime->end = instr->GetNumber();
  601. lifetime->totalOpHelperLengthByEnd = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(instr);
  602. }
  603. }
  604. else
  605. {
  606. // extend lifetime to the outer most loop boundary that have the symbol live on back edge.
  607. bool isLifetimeExtended = false;
  608. if (lifetime->start > extendedLifetimeStart)
  609. {
  610. isLifetimeExtended = true;
  611. lifetime->start = extendedLifetimeStart;
  612. }
  613. if (lifetime->end < extendedLifetimeEnd)
  614. {
  615. isLifetimeExtended = true;
  616. lifetime->end = extendedLifetimeEnd;
  617. // The total op helper length by the end of this lifetime will be updated once we reach the loop tail
  618. }
  619. if (isLifetimeExtended)
  620. {
  621. // Keep track of the lifetime extended for this loop so we can update the call bits
  622. FOREACH_SLISTBASE_ENTRY(Loop *, currLoop, this->extendedLifetimesLoopList)
  623. {
  624. currLoop->regAlloc.extendedLifetime->Prepend(lifetime);
  625. }
  626. NEXT_SLISTBASE_ENTRY
  627. }
  628. #if _M_ARM64
  629. // The case of equality is valid on Arm64 where some branch instructions have sources.
  630. AssertMsg(lifetime->end >= instr->GetNumber(), "Lifetime end not set correctly");
  631. #else
  632. AssertMsg(lifetime->end > instr->GetNumber(), "Lifetime end not set correctly");
  633. #endif
  634. }
  635. this->extendedLifetimesLoopList->Clear(this->tempAlloc);
  636. }
  637. // SCCLiveness::InsertLifetime
  638. // Insert a new lifetime in the list of lifetime. The lifetime are inserted
  639. // in the reverse order of the lifetime starts.
  640. Lifetime *
  641. SCCLiveness::InsertLifetime(StackSym *stackSym, RegNum reg, IR::Instr *const currentInstr)
  642. {
  643. const uint start = currentInstr->GetNumber(), end = start;
  644. Lifetime * newLlifetime = JitAnew(tempAlloc, Lifetime, tempAlloc, stackSym, reg, start, end);
  645. newLlifetime->totalOpHelperLengthByEnd = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(currentInstr);
  646. // Find insertion point
  647. // This looks like a search, but we should almost exit on the first iteration, except
  648. // when we have loops and some lifetimes where extended.
  649. FOREACH_SLIST_ENTRY_EDITING(Lifetime *, lifetime, &this->lifetimeList, iter)
  650. {
  651. if (lifetime->start <= start)
  652. {
  653. break;
  654. }
  655. }
  656. NEXT_SLIST_ENTRY_EDITING;
  657. iter.InsertBefore(newLlifetime);
  658. // let's say 'var a = 10;'. if a is not used in the function, we still want to have the instr, otherwise the write-through will not happen and upon debug bailout
  659. // we would not be able to restore the values to see in locals window.
  660. if (this->func->IsJitInDebugMode() && stackSym->HasByteCodeRegSlot() && this->func->IsNonTempLocalVar(stackSym->GetByteCodeRegSlot()))
  661. {
  662. newLlifetime->isDeadStore = false;
  663. }
  664. stackSym->scratch.linearScan.lifetime = newLlifetime;
  665. return newLlifetime;
  666. }
  667. bool
  668. SCCLiveness::FoldIndir(IR::Instr *instr, IR::Opnd *opnd)
  669. {
  670. #ifdef _M_ARM32_OR_ARM64
  671. // Can't be folded on ARM or ARM64
  672. return false;
  673. #else
  674. IR::IndirOpnd *indir = opnd->AsIndirOpnd();
  675. if(indir->GetIndexOpnd())
  676. {
  677. IR::RegOpnd *index = indir->GetIndexOpnd();
  678. if (!index->m_sym || !index->m_sym->IsIntConst())
  679. {
  680. return false;
  681. }
  682. // offset = indir.offset + (index << scale)
  683. int32 offset = index->m_sym->GetIntConstValue();
  684. if((indir->GetScale() != 0 && Int32Math::Shl(offset, indir->GetScale(), &offset)) ||
  685. (indir->GetOffset() != 0 && Int32Math::Add(indir->GetOffset(), offset, &offset)))
  686. {
  687. return false;
  688. }
  689. indir->SetOffset(offset);
  690. indir->SetIndexOpnd(nullptr);
  691. }
  692. IR::RegOpnd *base = indir->GetBaseOpnd();
  693. uint8 *constValue = nullptr;
  694. if (base)
  695. {
  696. if (!base->m_sym || !base->m_sym->IsConst() || base->m_sym->IsIntConst() || base->m_sym->IsFloatConst())
  697. {
  698. return false;
  699. }
  700. constValue = static_cast<uint8 *>(base->m_sym->GetConstAddress());
  701. if (indir->GetOffset() < 0 ? constValue + indir->GetOffset() > constValue : constValue + indir->GetOffset() < constValue)
  702. {
  703. return false;
  704. }
  705. }
  706. constValue += indir->GetOffset();
  707. #ifdef _M_X64
  708. // Encoding only allows 32bits worth
  709. if(!Math::FitsInDWord((size_t)constValue))
  710. {
  711. Assert(base != nullptr);
  712. return false;
  713. }
  714. #endif
  715. IR::MemRefOpnd *memref = IR::MemRefOpnd::New(constValue, indir->GetType(), instr->m_func);
  716. if (indir == instr->GetDst())
  717. {
  718. instr->ReplaceDst(memref);
  719. }
  720. else
  721. {
  722. instr->ReplaceSrc(indir, memref);
  723. }
  724. return true;
  725. #endif
  726. }
  727. uint SCCLiveness::CurrentOpHelperVisitedLength(IR::Instr *const currentInstr) const
  728. {
  729. Assert(currentInstr);
  730. if(!lastOpHelperLabel)
  731. {
  732. return 0;
  733. }
  734. Assert(currentInstr->GetNumber() >= lastOpHelperLabel->GetNumber());
  735. uint visitedLength = currentInstr->GetNumber() - lastOpHelperLabel->GetNumber();
  736. if(!currentInstr->IsLabelInstr())
  737. {
  738. // Consider the current instruction to have been visited
  739. ++visitedLength;
  740. }
  741. return visitedLength;
  742. }
  743. #if DBG_DUMP
  744. // SCCLiveness::Dump
  745. void
  746. SCCLiveness::Dump()
  747. {
  748. this->func->DumpHeader();
  749. Output::Print(_u("************ Liveness ************\n"));
  750. FOREACH_SLIST_ENTRY(Lifetime *, lifetime, &this->lifetimeList)
  751. {
  752. lifetime->sym->Dump();
  753. Output::Print(_u(": live range %3d - %3d (XUserCall: %d, XCall: %d)\n"), lifetime->start, lifetime->end,
  754. lifetime->isLiveAcrossUserCalls,
  755. lifetime->isLiveAcrossCalls);
  756. }
  757. NEXT_SLIST_ENTRY;
  758. FOREACH_INSTR_IN_FUNC(instr, func)
  759. {
  760. Output::Print(_u("%3d > "), instr->GetNumber());
  761. instr->Dump();
  762. } NEXT_INSTR_IN_FUNC;
  763. }
  764. #endif
  765. uint OpHelperBlock::Length() const
  766. {
  767. Assert(opHelperLabel);
  768. Assert(opHelperEndInstr);
  769. uint length = opHelperEndInstr->GetNumber() - opHelperLabel->GetNumber();
  770. if(!opHelperEndInstr->IsLabelInstr())
  771. {
  772. ++length;
  773. }
  774. return length;
  775. }