//===- AMDGPUReleaseVGPRs.cpp - Automatically release vgprs on GFX11+ -----===// // // 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 // //===----------------------------------------------------------------------===// // /// \file /// Insert S_SENDMSG instructions to release vgprs on GFX11+. // //===----------------------------------------------------------------------===// #include "AMDGPU.h" #include "AMDGPUSubtarget.h" #include "GCNSubtarget.h" #include "MCTargetDesc/AMDGPUMCTargetDesc.h" #include "SIDefines.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineOperand.h" #include using namespace llvm; #define DEBUG_TYPE "release-vgprs" namespace { class AMDGPUReleaseVGPRs : public MachineFunctionPass { public: static char ID; AMDGPUReleaseVGPRs() : MachineFunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); MachineFunctionPass::getAnalysisUsage(AU); } // Track if the last instruction referencing a vgpr in a MBB is a VMEM // store. Because this pass is late in the pipeline, it is expected that the // last vgpr use will likely be one of vmem store, ds, exp. // Loads and others vgpr operations would have been // deleted by this point, except for complex control flow involving loops. // This is why we are just testing the type of instructions rather // than the operands. class LastVGPRUseIsVMEMStore { BitVector BlockVMEMStore; static std::optional lastVGPRUseIsStore(const MachineBasicBlock &MBB) { for (auto &MI : reverse(MBB.instrs())) { // If it's a VMEM store, a VGPR will be used, return true. if ((SIInstrInfo::isVMEM(MI) || SIInstrInfo::isFLAT(MI)) && MI.mayStore()) return true; // If it's referencing a VGPR but is not a VMEM store, return false. if (SIInstrInfo::isDS(MI) || SIInstrInfo::isEXP(MI) || SIInstrInfo::isVMEM(MI) || SIInstrInfo::isFLAT(MI) || SIInstrInfo::isVALU(MI)) return false; } // Wait until the values are propagated from the predecessors return std::nullopt; } public: LastVGPRUseIsVMEMStore(const MachineFunction &MF) : BlockVMEMStore(MF.getNumBlockIDs()) { df_iterator_default_set Visited; SmallVector EndWithVMEMStoreBlocks; for (const auto &MBB : MF) { auto LastUseIsStore = lastVGPRUseIsStore(MBB); if (!LastUseIsStore.has_value()) continue; if (*LastUseIsStore) { EndWithVMEMStoreBlocks.push_back(&MBB); } else { Visited.insert(&MBB); } } for (const auto *MBB : EndWithVMEMStoreBlocks) { for (const auto *Succ : depth_first_ext(MBB, Visited)) { BlockVMEMStore[Succ->getNumber()] = true; } } } // Return true if the last instruction referencing a vgpr in this MBB // is a VMEM store, otherwise return false. bool isLastVGPRUseVMEMStore(const MachineBasicBlock &MBB) const { return BlockVMEMStore[MBB.getNumber()]; } }; static bool runOnMachineBasicBlock(MachineBasicBlock &MBB, const SIInstrInfo *SII, const LastVGPRUseIsVMEMStore &BlockVMEMStore) { bool Changed = false; for (auto &MI : MBB.terminators()) { // Look for S_ENDPGM instructions if (MI.getOpcode() == AMDGPU::S_ENDPGM || MI.getOpcode() == AMDGPU::S_ENDPGM_SAVED) { // If the last instruction using a VGPR in the block is a VMEM store, // release VGPRs. The VGPRs release will be placed just before ending // the program if (BlockVMEMStore.isLastVGPRUseVMEMStore(MBB)) { BuildMI(MBB, MI, DebugLoc(), SII->get(AMDGPU::S_SENDMSG)) .addImm(AMDGPU::SendMsg::ID_DEALLOC_VGPRS_GFX11Plus); Changed = true; } } } return Changed; } bool runOnMachineFunction(MachineFunction &MF) override { Function &F = MF.getFunction(); if (skipFunction(F) || !AMDGPU::isEntryFunctionCC(F.getCallingConv())) return false; // This pass only runs on GFX11+ const GCNSubtarget &ST = MF.getSubtarget(); if (ST.getGeneration() < AMDGPUSubtarget::GFX11) return false; LLVM_DEBUG(dbgs() << "AMDGPUReleaseVGPRs running on " << MF.getName() << "\n"); const SIInstrInfo *SII = ST.getInstrInfo(); LastVGPRUseIsVMEMStore BlockVMEMStore(MF); bool Changed = false; for (auto &MBB : MF) { Changed |= runOnMachineBasicBlock(MBB, SII, BlockVMEMStore); } return Changed; } }; } // namespace char AMDGPUReleaseVGPRs::ID = 0; char &llvm::AMDGPUReleaseVGPRsID = AMDGPUReleaseVGPRs::ID; INITIALIZE_PASS(AMDGPUReleaseVGPRs, DEBUG_TYPE, "Release VGPRs", false, false)