| 1 | //===- GCNCreateVOPD.cpp - Create VOPD Instructions ----------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | /// \file |
| 10 | /// Combine VALU pairs into VOPD instructions |
| 11 | /// Only works on wave32 |
| 12 | /// Has register requirements, we reject creating VOPD if the requirements are |
| 13 | /// not met. |
| 14 | /// shouldCombineVOPD mutator in postRA machine scheduler puts candidate |
| 15 | /// instructions for VOPD back-to-back |
| 16 | /// |
| 17 | // |
| 18 | //===----------------------------------------------------------------------===// |
| 19 | |
| 20 | #include "AMDGPU.h" |
| 21 | #include "GCNSubtarget.h" |
| 22 | #include "GCNVOPDUtils.h" |
| 23 | #include "SIInstrInfo.h" |
| 24 | #include "Utils/AMDGPUBaseInfo.h" |
| 25 | #include "llvm/ADT/SmallVector.h" |
| 26 | #include "llvm/ADT/Statistic.h" |
| 27 | #include "llvm/CodeGen/MachineBasicBlock.h" |
| 28 | #include "llvm/CodeGen/MachineInstr.h" |
| 29 | #include "llvm/CodeGen/MachineOperand.h" |
| 30 | #include "llvm/CodeGen/MachinePassManager.h" |
| 31 | #include "llvm/Support/Debug.h" |
| 32 | |
| 33 | #define DEBUG_TYPE "gcn-create-vopd" |
| 34 | STATISTIC(NumVOPDCreated, "Number of VOPD Insts Created." ); |
| 35 | |
| 36 | using namespace llvm; |
| 37 | |
| 38 | namespace { |
| 39 | |
| 40 | class GCNCreateVOPD { |
| 41 | private: |
| 42 | class VOPDCombineInfo { |
| 43 | public: |
| 44 | VOPDCombineInfo() = default; |
| 45 | VOPDCombineInfo(MachineInstr *First, MachineInstr *Second) |
| 46 | : FirstMI(First), SecondMI(Second) {} |
| 47 | |
| 48 | MachineInstr *FirstMI; |
| 49 | MachineInstr *SecondMI; |
| 50 | }; |
| 51 | |
| 52 | public: |
| 53 | const GCNSubtarget *ST = nullptr; |
| 54 | |
| 55 | bool doReplace(const SIInstrInfo *SII, VOPDCombineInfo &CI) { |
| 56 | auto *FirstMI = CI.FirstMI; |
| 57 | auto *SecondMI = CI.SecondMI; |
| 58 | unsigned Opc1 = FirstMI->getOpcode(); |
| 59 | unsigned Opc2 = SecondMI->getOpcode(); |
| 60 | unsigned EncodingFamily = |
| 61 | AMDGPU::getVOPDEncodingFamily(ST: SII->getSubtarget()); |
| 62 | int NewOpcode = |
| 63 | AMDGPU::getVOPDFull(OpX: AMDGPU::getVOPDOpcode(Opc: Opc1), |
| 64 | OpY: AMDGPU::getVOPDOpcode(Opc: Opc2), EncodingFamily); |
| 65 | assert(NewOpcode != -1 && |
| 66 | "Should have previously determined this as a possible VOPD\n" ); |
| 67 | |
| 68 | auto VOPDInst = BuildMI(BB&: *FirstMI->getParent(), I: FirstMI, |
| 69 | MIMD: FirstMI->getDebugLoc(), MCID: SII->get(Opcode: NewOpcode)) |
| 70 | .setMIFlags(FirstMI->getFlags() | SecondMI->getFlags()); |
| 71 | |
| 72 | namespace VOPD = AMDGPU::VOPD; |
| 73 | MachineInstr *MI[] = {FirstMI, SecondMI}; |
| 74 | auto InstInfo = |
| 75 | AMDGPU::getVOPDInstInfo(OpX: FirstMI->getDesc(), OpY: SecondMI->getDesc()); |
| 76 | |
| 77 | for (auto CompIdx : VOPD::COMPONENTS) { |
| 78 | auto MCOprIdx = InstInfo[CompIdx].getIndexOfDstInMCOperands(); |
| 79 | VOPDInst.add(MO: MI[CompIdx]->getOperand(i: MCOprIdx)); |
| 80 | } |
| 81 | |
| 82 | for (auto CompIdx : VOPD::COMPONENTS) { |
| 83 | auto CompSrcOprNum = InstInfo[CompIdx].getCompSrcOperandsNum(); |
| 84 | for (unsigned CompSrcIdx = 0; CompSrcIdx < CompSrcOprNum; ++CompSrcIdx) { |
| 85 | auto MCOprIdx = InstInfo[CompIdx].getIndexOfSrcInMCOperands(CompSrcIdx); |
| 86 | VOPDInst.add(MO: MI[CompIdx]->getOperand(i: MCOprIdx)); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | SII->fixImplicitOperands(MI&: *VOPDInst); |
| 91 | for (auto CompIdx : VOPD::COMPONENTS) |
| 92 | VOPDInst.copyImplicitOps(OtherMI: *MI[CompIdx]); |
| 93 | |
| 94 | LLVM_DEBUG(dbgs() << "VOPD Fused: " << *VOPDInst << " from\tX: " |
| 95 | << *CI.FirstMI << "\tY: " << *CI.SecondMI << "\n" ); |
| 96 | |
| 97 | for (auto CompIdx : VOPD::COMPONENTS) |
| 98 | MI[CompIdx]->eraseFromParent(); |
| 99 | |
| 100 | ++NumVOPDCreated; |
| 101 | return true; |
| 102 | } |
| 103 | |
| 104 | bool run(MachineFunction &MF) { |
| 105 | ST = &MF.getSubtarget<GCNSubtarget>(); |
| 106 | if (!AMDGPU::hasVOPD(STI: *ST) || !ST->isWave32()) |
| 107 | return false; |
| 108 | LLVM_DEBUG(dbgs() << "CreateVOPD Pass:\n" ); |
| 109 | |
| 110 | const SIInstrInfo *SII = ST->getInstrInfo(); |
| 111 | bool Changed = false; |
| 112 | |
| 113 | SmallVector<VOPDCombineInfo> ReplaceCandidates; |
| 114 | |
| 115 | for (auto &MBB : MF) { |
| 116 | auto MII = MBB.begin(), E = MBB.end(); |
| 117 | while (MII != E) { |
| 118 | auto *FirstMI = &*MII; |
| 119 | MII = next_nodbg(It: MII, End: MBB.end()); |
| 120 | if (MII == MBB.end()) |
| 121 | break; |
| 122 | if (FirstMI->isDebugInstr()) |
| 123 | continue; |
| 124 | auto *SecondMI = &*MII; |
| 125 | unsigned Opc = FirstMI->getOpcode(); |
| 126 | unsigned Opc2 = SecondMI->getOpcode(); |
| 127 | llvm::AMDGPU::CanBeVOPD FirstCanBeVOPD = AMDGPU::getCanBeVOPD(Opc); |
| 128 | llvm::AMDGPU::CanBeVOPD SecondCanBeVOPD = AMDGPU::getCanBeVOPD(Opc: Opc2); |
| 129 | VOPDCombineInfo CI; |
| 130 | |
| 131 | if (FirstCanBeVOPD.X && SecondCanBeVOPD.Y) |
| 132 | CI = VOPDCombineInfo(FirstMI, SecondMI); |
| 133 | else if (FirstCanBeVOPD.Y && SecondCanBeVOPD.X) |
| 134 | CI = VOPDCombineInfo(SecondMI, FirstMI); |
| 135 | else |
| 136 | continue; |
| 137 | // checkVOPDRegConstraints cares about program order, but doReplace |
| 138 | // cares about X-Y order in the constituted VOPD |
| 139 | if (llvm::checkVOPDRegConstraints(TII: *SII, FirstMI: *FirstMI, SecondMI: *SecondMI)) { |
| 140 | ReplaceCandidates.push_back(Elt: CI); |
| 141 | ++MII; |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | for (auto &CI : ReplaceCandidates) { |
| 146 | Changed |= doReplace(SII, CI); |
| 147 | } |
| 148 | |
| 149 | return Changed; |
| 150 | } |
| 151 | }; |
| 152 | |
| 153 | class GCNCreateVOPDLegacy : public MachineFunctionPass { |
| 154 | public: |
| 155 | static char ID; |
| 156 | GCNCreateVOPDLegacy() : MachineFunctionPass(ID) {} |
| 157 | |
| 158 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 159 | AU.setPreservesCFG(); |
| 160 | MachineFunctionPass::getAnalysisUsage(AU); |
| 161 | } |
| 162 | |
| 163 | StringRef getPassName() const override { |
| 164 | return "GCN Create VOPD Instructions" ; |
| 165 | } |
| 166 | bool runOnMachineFunction(MachineFunction &MF) override { |
| 167 | if (skipFunction(F: MF.getFunction())) |
| 168 | return false; |
| 169 | |
| 170 | return GCNCreateVOPD().run(MF); |
| 171 | } |
| 172 | }; |
| 173 | |
| 174 | } // namespace |
| 175 | |
| 176 | PreservedAnalyses |
| 177 | llvm::GCNCreateVOPDPass::run(MachineFunction &MF, |
| 178 | MachineFunctionAnalysisManager &AM) { |
| 179 | if (!GCNCreateVOPD().run(MF)) |
| 180 | return PreservedAnalyses::all(); |
| 181 | return getMachineFunctionPassPreservedAnalyses().preserveSet<CFGAnalyses>(); |
| 182 | } |
| 183 | |
| 184 | char GCNCreateVOPDLegacy::ID = 0; |
| 185 | |
| 186 | char &llvm::GCNCreateVOPDID = GCNCreateVOPDLegacy::ID; |
| 187 | |
| 188 | INITIALIZE_PASS(GCNCreateVOPDLegacy, DEBUG_TYPE, "GCN Create VOPD Instructions" , |
| 189 | false, false) |
| 190 | |