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"
34STATISTIC(NumVOPDCreated, "Number of VOPD Insts Created.");
35
36using namespace llvm;
37
38namespace {
39
40class GCNCreateVOPD {
41private:
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
52public:
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
153class GCNCreateVOPDLegacy : public MachineFunctionPass {
154public:
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
176PreservedAnalyses
177llvm::GCNCreateVOPDPass::run(MachineFunction &MF,
178 MachineFunctionAnalysisManager &AM) {
179 if (!GCNCreateVOPD().run(MF))
180 return PreservedAnalyses::all();
181 return getMachineFunctionPassPreservedAnalyses().preserveSet<CFGAnalyses>();
182}
183
184char GCNCreateVOPDLegacy::ID = 0;
185
186char &llvm::GCNCreateVOPDID = GCNCreateVOPDLegacy::ID;
187
188INITIALIZE_PASS(GCNCreateVOPDLegacy, DEBUG_TYPE, "GCN Create VOPD Instructions",
189 false, false)
190