| //===-- AMDGPUMarkLastScratchLoad.cpp -------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Mark scratch load/spill instructions which are guaranteed to be the last time | |
| // this scratch slot is used so it can be evicted from caches. | |
| // | |
| // TODO: Handle general stack accesses not just spilling. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "AMDGPU.h" | |
| #include "GCNSubtarget.h" | |
| #include "llvm/CodeGen/LiveIntervals.h" | |
| #include "llvm/CodeGen/LiveStacks.h" | |
| #include "llvm/CodeGen/MachineOperand.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "amdgpu-mark-last-scratch-load" | |
| namespace { | |
| class AMDGPUMarkLastScratchLoad { | |
| private: | |
| LiveStacks *LS = nullptr; | |
| LiveIntervals *LIS = nullptr; | |
| SlotIndexes *SI = nullptr; | |
| const SIInstrInfo *SII = nullptr; | |
| public: | |
| AMDGPUMarkLastScratchLoad(LiveStacks *LS, LiveIntervals *LIS, SlotIndexes *SI) | |
| : LS(LS), LIS(LIS), SI(SI) {} | |
| bool run(MachineFunction &MF); | |
| }; | |
| class AMDGPUMarkLastScratchLoadLegacy : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| AMDGPUMarkLastScratchLoadLegacy() : MachineFunctionPass(ID) {} | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.addRequired<SlotIndexesWrapperPass>(); | |
| AU.addRequired<LiveIntervalsWrapperPass>(); | |
| AU.addRequired<LiveStacksWrapperLegacy>(); | |
| AU.setPreservesAll(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| } | |
| StringRef getPassName() const override { | |
| return "AMDGPU Mark Last Scratch Load"; | |
| } | |
| }; | |
| } // end anonymous namespace | |
| bool AMDGPUMarkLastScratchLoadLegacy::runOnMachineFunction( | |
| MachineFunction &MF) { | |
| if (skipFunction(MF.getFunction())) | |
| return false; | |
| auto &LS = getAnalysis<LiveStacksWrapperLegacy>().getLS(); | |
| auto &LIS = getAnalysis<LiveIntervalsWrapperPass>().getLIS(); | |
| auto &SI = getAnalysis<SlotIndexesWrapperPass>().getSI(); | |
| return AMDGPUMarkLastScratchLoad(&LS, &LIS, &SI).run(MF); | |
| } | |
| PreservedAnalyses | |
| AMDGPUMarkLastScratchLoadPass::run(MachineFunction &MF, | |
| MachineFunctionAnalysisManager &MFAM) { | |
| auto &LS = MFAM.getResult<LiveStacksAnalysis>(MF); | |
| auto &LIS = MFAM.getResult<LiveIntervalsAnalysis>(MF); | |
| auto &SI = MFAM.getResult<SlotIndexesAnalysis>(MF); | |
| AMDGPUMarkLastScratchLoad(&LS, &LIS, &SI).run(MF); | |
| return PreservedAnalyses::all(); | |
| } | |
| bool AMDGPUMarkLastScratchLoad::run(MachineFunction &MF) { | |
| const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>(); | |
| if (ST.getGeneration() < AMDGPUSubtarget::GFX12) | |
| return false; | |
| SII = ST.getInstrInfo(); | |
| SlotIndexes &Slots = *LIS->getSlotIndexes(); | |
| const unsigned NumSlots = LS->getNumIntervals(); | |
| if (NumSlots == 0) { | |
| LLVM_DEBUG(dbgs() << "No live slots, skipping\n"); | |
| return false; | |
| } | |
| LLVM_DEBUG(dbgs() << LS->getNumIntervals() << " intervals\n"); | |
| bool Changed = false; | |
| for (auto &[SS, LI] : *LS) { | |
| for (const LiveRange::Segment &Segment : LI.segments) { | |
| // Ignore segments that run to the end of basic block because in this case | |
| // slot is still live at the end of it. | |
| if (Segment.end.isBlock()) | |
| continue; | |
| const int FrameIndex = LI.reg().stackSlotIndex(); | |
| MachineInstr *LastLoad = nullptr; | |
| MachineInstr *MISegmentEnd = SI->getInstructionFromIndex(Segment.end); | |
| // If there is no instruction at this slot because it was deleted take the | |
| // instruction from the next slot. | |
| if (!MISegmentEnd) { | |
| SlotIndex NextSlot = Slots.getNextNonNullIndex(Segment.end); | |
| MISegmentEnd = SI->getInstructionFromIndex(NextSlot); | |
| } | |
| MachineInstr *MISegmentStart = SI->getInstructionFromIndex(Segment.start); | |
| MachineBasicBlock *BB = MISegmentEnd->getParent(); | |
| // Start iteration backwards from segment end until the start of basic | |
| // block or start of segment if it is in the same basic block. | |
| auto End = BB->rend(); | |
| if (MISegmentStart && MISegmentStart->getParent() == BB) | |
| End = MISegmentStart->getReverseIterator(); | |
| for (auto MI = MISegmentEnd->getReverseIterator(); MI != End; ++MI) { | |
| int LoadFI = 0; | |
| if (SII->isLoadFromStackSlot(*MI, LoadFI) && LoadFI == FrameIndex) { | |
| LastLoad = &*MI; | |
| break; | |
| } | |
| } | |
| if (LastLoad && !LastLoad->memoperands_empty()) { | |
| MachineMemOperand *MMO = *LastLoad->memoperands_begin(); | |
| MMO->setFlags(MOLastUse); | |
| Changed = true; | |
| LLVM_DEBUG(dbgs() << " Found last load: " << *LastLoad); | |
| } | |
| } | |
| } | |
| return Changed; | |
| } | |
| char AMDGPUMarkLastScratchLoadLegacy::ID = 0; | |
| char &llvm::AMDGPUMarkLastScratchLoadID = AMDGPUMarkLastScratchLoadLegacy::ID; | |
| INITIALIZE_PASS_BEGIN(AMDGPUMarkLastScratchLoadLegacy, DEBUG_TYPE, | |
| "AMDGPU Mark last scratch load", false, false) | |
| INITIALIZE_PASS_DEPENDENCY(SlotIndexesWrapperPass) | |
| INITIALIZE_PASS_DEPENDENCY(LiveStacksWrapperLegacy) | |
| INITIALIZE_PASS_END(AMDGPUMarkLastScratchLoadLegacy, DEBUG_TYPE, | |
| "AMDGPU Mark last scratch load", false, false) |