SCCLiveness.cpp 28 KB

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