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 | |