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