@@ -88,6 +88,19 @@ struct CandidateInfo {
8888 MachineInstr *HoistCandidate = nullptr ;
8989};
9090
91+ // / Describes a matched save/restore bracket for a reserved register:
92+ // / save: vreg = COPY $reserved_reg (first use in latch, before set)
93+ // / set: $reserved_reg = <imm> (loop-invariant, immediately after)
94+ // / restore: $reserved_reg = COPY vreg (last def in latch)
95+ // / The transformation hoists save+set to the preheader and sinks restore to
96+ // / the exit block, so the loop body always sees the loop-invariant value.
97+ struct SaveRestoreBracket {
98+ MachineInstr *Save = nullptr ;
99+ MachineInstr *Set = nullptr ;
100+ MachineInstr *Restore = nullptr ;
101+ MCPhysReg Reg = MCRegister::NoRegister;
102+ };
103+
91104// / Used to collect candidates for hoisting/sinking
92105class Candidates {
93106 DenseMap<MCPhysReg, CandidateInfo> Candidates;
@@ -163,6 +176,19 @@ class ReservedRegsLICM : public MachineFunctionPass {
163176 // / Hoist \p Cand to \p L's preheader if it is safe to do so.
164177 bool tryHoistToPreHeader (const CandidateInfo &Cand, MachineLoop &L);
165178
179+ // / Detect and transform a save/restore bracket in \p L's latch block.
180+ // / A bracket has the form:
181+ // / vreg = COPY $reserved_reg (save)
182+ // / $reserved_reg = <imm> (set, loop-invariant)
183+ // / ...uses of $reserved_reg...
184+ // / $reserved_reg = COPY vreg (restore)
185+ // / Returns the matched bracket, or std::nullopt if not found.
186+ std::optional<SaveRestoreBracket> findSaveRestoreBracket (MachineLoop &L);
187+
188+ // / Apply the save/restore bracket transformation: hoist save+set to the
189+ // / preheader and sink restore to the exit block.
190+ bool processSaveRestoreBracket (MachineLoop &L);
191+
166192 // / Verify if \p Cand is loop invariant and can be safely hoisted.
167193 // / \pre Cand->DefinedReg has a unique live value within the loop. This is
168194 // / verified by processForExitSink() or processForPreheaderHoist().
@@ -257,8 +283,14 @@ void ReservedRegsLICM::runOnLoop(MachineLoop &L) {
257283 BitVector ReservedLiveins = collectLoopReservedLiveins (L);
258284 processForExitSink (L, ReservedLiveins);
259285 processForPreheaderHoist (L, ReservedLiveins);
286+ Changed |= processSaveRestoreBracket (L);
260287}
261288
289+ // Forward declaration — defined after processForPreheaderHoist.
290+ static void moveInstruction (const CandidateInfo &Cand,
291+ MachineBasicBlock::iterator InsertBefore,
292+ MachineBasicBlock &InsertMBB);
293+
262294void ReservedRegsLICM::processForExitSink (MachineLoop &L,
263295 const BitVector &ReservedLiveins) {
264296 // Pre-compute what's live at the entry of the latch block by walking it
@@ -305,6 +337,145 @@ void ReservedRegsLICM::processForExitSink(MachineLoop &L,
305337 }
306338}
307339
340+ std::optional<SaveRestoreBracket>
341+ ReservedRegsLICM::findSaveRestoreBracket (MachineLoop &L) {
342+ MachineBasicBlock *Latch = L.getLoopLatch ();
343+ assert (Latch);
344+
345+ for (MachineInstr &SaveMI : *Latch) {
346+ // Look for: vreg = COPY $reserved_reg
347+ if (!SaveMI.isCopy ())
348+ continue ;
349+ Register SaveDst = SaveMI.getOperand (0 ).getReg ();
350+ Register SaveSrc = SaveMI.getOperand (1 ).getReg ();
351+ if (!SaveDst.isVirtual () || !SaveSrc.isPhysical ())
352+ continue ;
353+ MCPhysReg PhysReg = SaveSrc.asMCReg ();
354+ if (!TRI->isSimplifiableReservedReg (PhysReg))
355+ continue ;
356+
357+ // The saved vreg must have exactly one non-debug use (the restore).
358+ if (!MRI->hasOneNonDBGUse (SaveDst))
359+ continue ;
360+
361+ // Helper: does MI use PhysReg?
362+ auto UsesPhysReg = [&](const MachineInstr &MI) {
363+ return any_of (MI.operands (), [&](const MachineOperand &MO) {
364+ return MO.isReg () && MO.isUse () && MO.getReg () == PhysReg;
365+ });
366+ };
367+
368+ // No uses of PhysReg before the save. Instructions before the save
369+ // rely on the original value of PhysReg; hoisting the set would make
370+ // them see the loop-invariant value instead.
371+ auto BeforeSave =
372+ make_range (Latch->begin (), MachineBasicBlock::iterator (SaveMI));
373+ if (any_of (BeforeSave, UsesPhysReg))
374+ continue ;
375+
376+ // Find the set: the next def of PhysReg after the save.
377+ // No use of PhysReg is allowed between the save and the set.
378+ MachineInstr *SetMI = nullptr ;
379+ bool PhysRegUsedBeforeSet = false ;
380+ for (MachineInstr *Next = SaveMI.getNextNode (); Next;
381+ Next = Next->getNextNode ()) {
382+ if (getSinglePhysRegDef (*Next) == PhysReg) {
383+ SetMI = Next;
384+ break ;
385+ }
386+ if (UsesPhysReg (*Next)) {
387+ PhysRegUsedBeforeSet = true ;
388+ break ;
389+ }
390+ }
391+ if (!SetMI || PhysRegUsedBeforeSet)
392+ continue ;
393+
394+ // The set must be loop-invariant (e.g. MOVX imm).
395+ CandidateInfo SetCand (PhysReg);
396+ SetCand.HoistCandidate = SetMI;
397+ if (!isLoopInvariantInst (SetCand, L))
398+ continue ;
399+
400+ // Find the restore: the last def of PhysReg in the latch.
401+ // It must be: $reserved_reg = COPY SaveDst.
402+ MachineInstr *RestoreMI = nullptr ;
403+ for (MachineInstr &MI2 : reverse (*Latch)) {
404+ if (getSinglePhysRegDef (MI2) == PhysReg) {
405+ if (MI2.isCopy () && MI2.getOperand (1 ).getReg () == SaveDst)
406+ RestoreMI = &MI2;
407+ break ;
408+ }
409+ }
410+ if (!RestoreMI)
411+ continue ;
412+
413+ // No uses of PhysReg after the restore. Once the restore is sinked to
414+ // the exit block, instructions after the restore position in the loop
415+ // body would see the loop-invariant value (from the set) instead of
416+ // the restored value.
417+ auto AfterRestore = make_range (
418+ std::next (MachineBasicBlock::iterator (RestoreMI)), Latch->end ());
419+ if (any_of (AfterRestore, UsesPhysReg))
420+ continue ;
421+
422+ // PhysReg must not be used in any non-latch block of the loop.
423+ // If it were, those uses might see the wrong value after we hoist
424+ // the set to the preheader.
425+ if (any_of (L.getBlocks (), [&](MachineBasicBlock *MBB) {
426+ return MBB != Latch && any_of (*MBB, UsesPhysReg);
427+ }))
428+ continue ;
429+
430+ return SaveRestoreBracket{&SaveMI, SetMI, RestoreMI, PhysReg};
431+ }
432+ return std::nullopt ;
433+ }
434+
435+ bool ReservedRegsLICM::processSaveRestoreBracket (MachineLoop &L) {
436+ std::optional<SaveRestoreBracket> BracketOpt = findSaveRestoreBracket (L);
437+ if (!BracketOpt)
438+ return false ;
439+
440+ const SaveRestoreBracket &Bracket = *BracketOpt;
441+ MachineBasicBlock *Preheader = L.getLoopPreheader ();
442+ MachineBasicBlock *ExitMBB = L.getExitBlock ();
443+ assert (Preheader && ExitMBB);
444+
445+ // Ensure the exit block is a dedicated exit (single predecessor).
446+ // runOnLoop already verified that the critical edge can be split if needed.
447+ if (!ExitMBB->getSinglePredecessor ()) {
448+ MachineBasicBlock *ExitingBlock = L.getExitingBlock ();
449+ ExitMBB = ExitingBlock->SplitCriticalEdge (ExitMBB, *this );
450+ assert (ExitMBB);
451+ LLVM_DEBUG (dbgs () << " Created dedicated exit: "
452+ << printMBBReference (*ExitMBB) << " \n " );
453+ }
454+
455+ LLVM_DEBUG (dbgs () << " Save/restore bracket for " << TRI->getName (Bracket.Reg )
456+ << " :\n "
457+ << " Save: " << *Bracket.Save << " Set: "
458+ << *Bracket.Set << " Restore: " << *Bracket.Restore );
459+
460+ // Move save + set to the preheader (save first so it captures the
461+ // pre-loop value before the set overwrites it).
462+ auto InsertPt = Preheader->getFirstTerminator ();
463+ CandidateInfo SaveCand (Bracket.Reg );
464+ SaveCand.HoistCandidate = Bracket.Save ;
465+ moveInstruction (SaveCand, InsertPt, *Preheader);
466+
467+ CandidateInfo SetCand (Bracket.Reg );
468+ SetCand.HoistCandidate = Bracket.Set ;
469+ moveInstruction (SetCand, InsertPt, *Preheader);
470+
471+ // Sink restore to the exit block.
472+ CandidateInfo RestoreCand (Bracket.Reg );
473+ RestoreCand.HoistCandidate = Bracket.Restore ;
474+ moveInstruction (RestoreCand, ExitMBB->getFirstNonPHI (), *ExitMBB);
475+
476+ return true ;
477+ }
478+
308479void ReservedRegsLICM::processForPreheaderHoist (
309480 MachineLoop &L, const BitVector &ReservedLiveins) {
310481 Candidates HoistCandidates;
@@ -333,9 +504,9 @@ void ReservedRegsLICM::processForPreheaderHoist(
333504// / When an instruction is found to only use loop invariant operands that is
334505// / safe to hoist/sink, this function is called to actually move the MI out of
335506// / the loop.
336- void moveInstruction (const CandidateInfo &Cand,
337- MachineBasicBlock::iterator InsertBefore,
338- MachineBasicBlock &InsertMBB) {
507+ static void moveInstruction (const CandidateInfo &Cand,
508+ MachineBasicBlock::iterator InsertBefore,
509+ MachineBasicBlock &InsertMBB) {
339510 MachineInstr &MI = *Cand.HoistCandidate ;
340511 LLVM_DEBUG (dbgs () << " Moving to " << printMBBReference (InsertMBB) << " from "
341512 << printMBBReference (*MI.getParent ()) << " : " << MI);
0 commit comments