1*a58f00eaSDimitry Andric //===-- AMDGPUMarkLastScratchLoad.cpp -------------------------------------===//
2*a58f00eaSDimitry Andric //
3*a58f00eaSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*a58f00eaSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*a58f00eaSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*a58f00eaSDimitry Andric //
7*a58f00eaSDimitry Andric //===----------------------------------------------------------------------===//
8*a58f00eaSDimitry Andric //
9*a58f00eaSDimitry Andric // Mark scratch load/spill instructions which are guaranteed to be the last time
10*a58f00eaSDimitry Andric // this scratch slot is used so it can be evicted from caches.
11*a58f00eaSDimitry Andric //
12*a58f00eaSDimitry Andric // TODO: Handle general stack accesses not just spilling.
13*a58f00eaSDimitry Andric //
14*a58f00eaSDimitry Andric //===----------------------------------------------------------------------===//
15*a58f00eaSDimitry Andric 
16*a58f00eaSDimitry Andric #include "AMDGPU.h"
17*a58f00eaSDimitry Andric #include "GCNSubtarget.h"
18*a58f00eaSDimitry Andric #include "llvm/CodeGen/LiveIntervals.h"
19*a58f00eaSDimitry Andric #include "llvm/CodeGen/LiveStacks.h"
20*a58f00eaSDimitry Andric #include "llvm/CodeGen/MachineOperand.h"
21*a58f00eaSDimitry Andric 
22*a58f00eaSDimitry Andric using namespace llvm;
23*a58f00eaSDimitry Andric 
24*a58f00eaSDimitry Andric #define DEBUG_TYPE "amdgpu-mark-last-scratch-load"
25*a58f00eaSDimitry Andric 
26*a58f00eaSDimitry Andric namespace {
27*a58f00eaSDimitry Andric 
28*a58f00eaSDimitry Andric class AMDGPUMarkLastScratchLoad : public MachineFunctionPass {
29*a58f00eaSDimitry Andric private:
30*a58f00eaSDimitry Andric   LiveStacks *LS = nullptr;
31*a58f00eaSDimitry Andric   LiveIntervals *LIS = nullptr;
32*a58f00eaSDimitry Andric   SlotIndexes *SI = nullptr;
33*a58f00eaSDimitry Andric   const SIInstrInfo *SII = nullptr;
34*a58f00eaSDimitry Andric 
35*a58f00eaSDimitry Andric public:
36*a58f00eaSDimitry Andric   static char ID;
37*a58f00eaSDimitry Andric 
AMDGPUMarkLastScratchLoad()38*a58f00eaSDimitry Andric   AMDGPUMarkLastScratchLoad() : MachineFunctionPass(ID) {
39*a58f00eaSDimitry Andric     initializeAMDGPUMarkLastScratchLoadPass(*PassRegistry::getPassRegistry());
40*a58f00eaSDimitry Andric   }
41*a58f00eaSDimitry Andric 
42*a58f00eaSDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
43*a58f00eaSDimitry Andric 
getAnalysisUsage(AnalysisUsage & AU) const44*a58f00eaSDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
45*a58f00eaSDimitry Andric     AU.addRequired<SlotIndexes>();
46*a58f00eaSDimitry Andric     AU.addRequired<LiveIntervals>();
47*a58f00eaSDimitry Andric     AU.addRequired<LiveStacks>();
48*a58f00eaSDimitry Andric     AU.setPreservesAll();
49*a58f00eaSDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
50*a58f00eaSDimitry Andric   }
51*a58f00eaSDimitry Andric 
getPassName() const52*a58f00eaSDimitry Andric   StringRef getPassName() const override {
53*a58f00eaSDimitry Andric     return "AMDGPU Mark Last Scratch Load";
54*a58f00eaSDimitry Andric   }
55*a58f00eaSDimitry Andric };
56*a58f00eaSDimitry Andric 
57*a58f00eaSDimitry Andric } // end anonymous namespace
58*a58f00eaSDimitry Andric 
runOnMachineFunction(MachineFunction & MF)59*a58f00eaSDimitry Andric bool AMDGPUMarkLastScratchLoad::runOnMachineFunction(MachineFunction &MF) {
60*a58f00eaSDimitry Andric   if (skipFunction(MF.getFunction()))
61*a58f00eaSDimitry Andric     return false;
62*a58f00eaSDimitry Andric 
63*a58f00eaSDimitry Andric   const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
64*a58f00eaSDimitry Andric   if (ST.getGeneration() < AMDGPUSubtarget::GFX12)
65*a58f00eaSDimitry Andric     return false;
66*a58f00eaSDimitry Andric 
67*a58f00eaSDimitry Andric   LS = &getAnalysis<LiveStacks>();
68*a58f00eaSDimitry Andric   LIS = &getAnalysis<LiveIntervals>();
69*a58f00eaSDimitry Andric   SI = &getAnalysis<SlotIndexes>();
70*a58f00eaSDimitry Andric   SII = ST.getInstrInfo();
71*a58f00eaSDimitry Andric   SlotIndexes &Slots = *LIS->getSlotIndexes();
72*a58f00eaSDimitry Andric 
73*a58f00eaSDimitry Andric   const unsigned NumSlots = LS->getNumIntervals();
74*a58f00eaSDimitry Andric   if (NumSlots == 0) {
75*a58f00eaSDimitry Andric     LLVM_DEBUG(dbgs() << "No live slots, skipping\n");
76*a58f00eaSDimitry Andric     return false;
77*a58f00eaSDimitry Andric   }
78*a58f00eaSDimitry Andric 
79*a58f00eaSDimitry Andric   LLVM_DEBUG(dbgs() << LS->getNumIntervals() << " intervals\n");
80*a58f00eaSDimitry Andric 
81*a58f00eaSDimitry Andric   bool Changed = false;
82*a58f00eaSDimitry Andric 
83*a58f00eaSDimitry Andric   for (auto &[SS, LI] : *LS) {
84*a58f00eaSDimitry Andric     for (const LiveRange::Segment &Segment : LI.segments) {
85*a58f00eaSDimitry Andric 
86*a58f00eaSDimitry Andric       // Ignore segments that run to the end of basic block because in this case
87*a58f00eaSDimitry Andric       // slot is still live at the end of it.
88*a58f00eaSDimitry Andric       if (Segment.end.isBlock())
89*a58f00eaSDimitry Andric         continue;
90*a58f00eaSDimitry Andric 
91*a58f00eaSDimitry Andric       const int FrameIndex = Register::stackSlot2Index(LI.reg());
92*a58f00eaSDimitry Andric       MachineInstr *LastLoad = nullptr;
93*a58f00eaSDimitry Andric 
94*a58f00eaSDimitry Andric       MachineInstr *MISegmentEnd = SI->getInstructionFromIndex(Segment.end);
95*a58f00eaSDimitry Andric 
96*a58f00eaSDimitry Andric       // If there is no instruction at this slot because it was deleted take the
97*a58f00eaSDimitry Andric       // instruction from the next slot.
98*a58f00eaSDimitry Andric       if (!MISegmentEnd) {
99*a58f00eaSDimitry Andric         SlotIndex NextSlot = Slots.getNextNonNullIndex(Segment.end);
100*a58f00eaSDimitry Andric         MISegmentEnd = SI->getInstructionFromIndex(NextSlot);
101*a58f00eaSDimitry Andric       }
102*a58f00eaSDimitry Andric 
103*a58f00eaSDimitry Andric       MachineInstr *MISegmentStart = SI->getInstructionFromIndex(Segment.start);
104*a58f00eaSDimitry Andric       MachineBasicBlock *BB = MISegmentEnd->getParent();
105*a58f00eaSDimitry Andric 
106*a58f00eaSDimitry Andric       // Start iteration backwards from segment end until the start of basic
107*a58f00eaSDimitry Andric       // block or start of segment if it is in the same basic block.
108*a58f00eaSDimitry Andric       auto End = BB->rend();
109*a58f00eaSDimitry Andric       if (MISegmentStart && MISegmentStart->getParent() == BB)
110*a58f00eaSDimitry Andric         End = MISegmentStart->getReverseIterator();
111*a58f00eaSDimitry Andric 
112*a58f00eaSDimitry Andric       for (auto MI = MISegmentEnd->getReverseIterator(); MI != End; ++MI) {
113*a58f00eaSDimitry Andric         int LoadFI = 0;
114*a58f00eaSDimitry Andric 
115*a58f00eaSDimitry Andric         if (SII->isLoadFromStackSlot(*MI, LoadFI) && LoadFI == FrameIndex) {
116*a58f00eaSDimitry Andric           LastLoad = &*MI;
117*a58f00eaSDimitry Andric           break;
118*a58f00eaSDimitry Andric         }
119*a58f00eaSDimitry Andric       }
120*a58f00eaSDimitry Andric 
121*a58f00eaSDimitry Andric       if (LastLoad && !LastLoad->memoperands_empty()) {
122*a58f00eaSDimitry Andric         MachineMemOperand *MMO = *LastLoad->memoperands_begin();
123*a58f00eaSDimitry Andric         MMO->setFlags(MOLastUse);
124*a58f00eaSDimitry Andric         Changed = true;
125*a58f00eaSDimitry Andric         LLVM_DEBUG(dbgs() << "  Found last load: " << *LastLoad);
126*a58f00eaSDimitry Andric       }
127*a58f00eaSDimitry Andric     }
128*a58f00eaSDimitry Andric   }
129*a58f00eaSDimitry Andric 
130*a58f00eaSDimitry Andric   return Changed;
131*a58f00eaSDimitry Andric }
132*a58f00eaSDimitry Andric 
133*a58f00eaSDimitry Andric char AMDGPUMarkLastScratchLoad::ID = 0;
134*a58f00eaSDimitry Andric 
135*a58f00eaSDimitry Andric char &llvm::AMDGPUMarkLastScratchLoadID = AMDGPUMarkLastScratchLoad::ID;
136*a58f00eaSDimitry Andric 
137*a58f00eaSDimitry Andric INITIALIZE_PASS_BEGIN(AMDGPUMarkLastScratchLoad, DEBUG_TYPE,
138*a58f00eaSDimitry Andric                       "AMDGPU Mark last scratch load", false, false)
139*a58f00eaSDimitry Andric INITIALIZE_PASS_DEPENDENCY(SlotIndexes)
140*a58f00eaSDimitry Andric INITIALIZE_PASS_DEPENDENCY(LiveStacks)
141*a58f00eaSDimitry Andric INITIALIZE_PASS_END(AMDGPUMarkLastScratchLoad, DEBUG_TYPE,
142*a58f00eaSDimitry Andric                     "AMDGPU Mark last scratch load", false, false)
143