SccLiveness.cpp 31 KB

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