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