SccLiveness.cpp 32 KB

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