1 //===-- SIPreEmitPeephole.cpp ------------------------------------===//
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 /// This pass performs the peephole optimizations before code emission.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "AMDGPU.h"
15 #include "AMDGPUSubtarget.h"
16 #include "MCTargetDesc/AMDGPUMCTargetDesc.h"
17 #include "SIInstrInfo.h"
18 #include "llvm/CodeGen/MachineFunctionPass.h"
19 #include "llvm/Support/CommandLine.h"
20 
21 using namespace llvm;
22 
23 #define DEBUG_TYPE "si-pre-emit-peephole"
24 
25 namespace {
26 
27 class SIPreEmitPeephole : public MachineFunctionPass {
28 private:
29   const SIInstrInfo *TII = nullptr;
30   const SIRegisterInfo *TRI = nullptr;
31 
32   bool optimizeVccBranch(MachineInstr &MI) const;
33   bool optimizeSetGPR(MachineInstr &First, MachineInstr &MI) const;
34 
35 public:
36   static char ID;
37 
38   SIPreEmitPeephole() : MachineFunctionPass(ID) {
39     initializeSIPreEmitPeepholePass(*PassRegistry::getPassRegistry());
40   }
41 
42   bool runOnMachineFunction(MachineFunction &MF) override;
43 };
44 
45 } // End anonymous namespace.
46 
47 INITIALIZE_PASS(SIPreEmitPeephole, DEBUG_TYPE,
48                 "SI peephole optimizations", false, false)
49 
50 char SIPreEmitPeephole::ID = 0;
51 
52 char &llvm::SIPreEmitPeepholeID = SIPreEmitPeephole::ID;
53 
54 bool SIPreEmitPeephole::optimizeVccBranch(MachineInstr &MI) const {
55   // Match:
56   // sreg = -1
57   // vcc = S_AND_B64 exec, sreg
58   // S_CBRANCH_VCC[N]Z
59   // =>
60   // S_CBRANCH_EXEC[N]Z
61   // We end up with this pattern sometimes after basic block placement.
62   // It happens while combining a block which assigns -1 to a saved mask and
63   // another block which consumes that saved mask and then a branch.
64   bool Changed = false;
65   MachineBasicBlock &MBB = *MI.getParent();
66   const GCNSubtarget &ST = MBB.getParent()->getSubtarget<GCNSubtarget>();
67   const bool IsWave32 = ST.isWave32();
68   const unsigned CondReg = TRI->getVCC();
69   const unsigned ExecReg = IsWave32 ? AMDGPU::EXEC_LO : AMDGPU::EXEC;
70   const unsigned And = IsWave32 ? AMDGPU::S_AND_B32 : AMDGPU::S_AND_B64;
71 
72   MachineBasicBlock::reverse_iterator A = MI.getReverseIterator(),
73                                       E = MBB.rend();
74   bool ReadsCond = false;
75   unsigned Threshold = 5;
76   for (++A; A != E; ++A) {
77     if (!--Threshold)
78       return false;
79     if (A->modifiesRegister(ExecReg, TRI))
80       return false;
81     if (A->modifiesRegister(CondReg, TRI)) {
82       if (!A->definesRegister(CondReg, TRI) || A->getOpcode() != And)
83         return false;
84       break;
85     }
86     ReadsCond |= A->readsRegister(CondReg, TRI);
87   }
88   if (A == E)
89     return false;
90 
91   MachineOperand &Op1 = A->getOperand(1);
92   MachineOperand &Op2 = A->getOperand(2);
93   if (Op1.getReg() != ExecReg && Op2.isReg() && Op2.getReg() == ExecReg) {
94     TII->commuteInstruction(*A);
95     Changed = true;
96   }
97   if (Op1.getReg() != ExecReg)
98     return Changed;
99   if (Op2.isImm() && Op2.getImm() != -1)
100     return Changed;
101 
102   Register SReg;
103   if (Op2.isReg()) {
104     SReg = Op2.getReg();
105     auto M = std::next(A);
106     bool ReadsSreg = false;
107     for (; M != E; ++M) {
108       if (M->definesRegister(SReg, TRI))
109         break;
110       if (M->modifiesRegister(SReg, TRI))
111         return Changed;
112       ReadsSreg |= M->readsRegister(SReg, TRI);
113     }
114     if (M == E || !M->isMoveImmediate() || !M->getOperand(1).isImm() ||
115         M->getOperand(1).getImm() != -1)
116       return Changed;
117     // First if sreg is only used in and instruction fold the immediate
118     // into that and.
119     if (!ReadsSreg && Op2.isKill()) {
120       A->getOperand(2).ChangeToImmediate(-1);
121       M->eraseFromParent();
122     }
123   }
124 
125   if (!ReadsCond && A->registerDefIsDead(AMDGPU::SCC) &&
126       MI.killsRegister(CondReg, TRI))
127     A->eraseFromParent();
128 
129   bool IsVCCZ = MI.getOpcode() == AMDGPU::S_CBRANCH_VCCZ;
130   if (SReg == ExecReg) {
131     if (IsVCCZ) {
132       MI.eraseFromParent();
133       return true;
134     }
135     MI.setDesc(TII->get(AMDGPU::S_BRANCH));
136   } else {
137     MI.setDesc(
138         TII->get(IsVCCZ ? AMDGPU::S_CBRANCH_EXECZ : AMDGPU::S_CBRANCH_EXECNZ));
139   }
140 
141   MI.RemoveOperand(MI.findRegisterUseOperandIdx(CondReg, false /*Kill*/, TRI));
142   MI.addImplicitDefUseOperands(*MBB.getParent());
143 
144   return true;
145 }
146 
147 bool SIPreEmitPeephole::optimizeSetGPR(MachineInstr &First,
148                                        MachineInstr &MI) const {
149   MachineBasicBlock &MBB = *MI.getParent();
150   const MachineFunction &MF = *MBB.getParent();
151   const MachineRegisterInfo &MRI = MF.getRegInfo();
152   MachineOperand *Idx = TII->getNamedOperand(MI, AMDGPU::OpName::src0);
153   Register IdxReg = Idx->isReg() ? Idx->getReg() : Register();
154   SmallVector<MachineInstr *, 4> ToRemove;
155   bool IdxOn = true;
156 
157   if (!MI.isIdenticalTo(First))
158     return false;
159 
160   // Scan back to find an identical S_SET_GPR_IDX_ON
161   for (MachineBasicBlock::iterator I = std::next(First.getIterator()),
162        E = MI.getIterator(); I != E; ++I) {
163     switch (I->getOpcode()) {
164     case AMDGPU::S_SET_GPR_IDX_MODE:
165       return false;
166     case AMDGPU::S_SET_GPR_IDX_OFF:
167       IdxOn = false;
168       ToRemove.push_back(&*I);
169       break;
170     default:
171       if (I->modifiesRegister(AMDGPU::M0, TRI))
172         return false;
173       if (IdxReg && I->modifiesRegister(IdxReg, TRI))
174         return false;
175       if (llvm::any_of(I->operands(),
176                        [&MRI, this](const MachineOperand &MO) {
177                          return MO.isReg() &&
178                                 TRI->isVectorRegister(MRI, MO.getReg());
179                        })) {
180         // The only exception allowed here is another indirect vector move
181         // with the same mode.
182         if (!IdxOn ||
183             !((I->getOpcode() == AMDGPU::V_MOV_B32_e32 &&
184                I->hasRegisterImplicitUseOperand(AMDGPU::M0)) ||
185               I->getOpcode() == AMDGPU::V_MOV_B32_indirect))
186           return false;
187       }
188     }
189   }
190 
191   MI.eraseFromParent();
192   for (MachineInstr *RI : ToRemove)
193     RI->eraseFromParent();
194   return true;
195 }
196 
197 bool SIPreEmitPeephole::runOnMachineFunction(MachineFunction &MF) {
198   const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
199   TII = ST.getInstrInfo();
200   TRI = &TII->getRegisterInfo();
201   bool Changed = false;
202 
203   for (MachineBasicBlock &MBB : MF) {
204     MachineBasicBlock::iterator MBBE = MBB.getFirstTerminator();
205     if (MBBE != MBB.end()) {
206       MachineInstr &MI = *MBBE;
207       switch (MI.getOpcode()) {
208       case AMDGPU::S_CBRANCH_VCCZ:
209       case AMDGPU::S_CBRANCH_VCCNZ:
210         Changed |= optimizeVccBranch(MI);
211         continue;
212       default:
213         break;
214       }
215     }
216 
217     if (!ST.hasVGPRIndexMode())
218       continue;
219 
220     MachineInstr *SetGPRMI = nullptr;
221     const unsigned Threshold = 20;
222     unsigned Count = 0;
223     // Scan the block for two S_SET_GPR_IDX_ON instructions to see if a
224     // second is not needed. Do expensive checks in the optimizeSetGPR()
225     // and limit the distance to 20 instructions for compile time purposes.
226     for (MachineBasicBlock::iterator MBBI = MBB.begin(); MBBI != MBBE; ) {
227       MachineInstr &MI = *MBBI;
228       ++MBBI;
229 
230       if (Count == Threshold)
231         SetGPRMI = nullptr;
232       else
233         ++Count;
234 
235       if (MI.getOpcode() != AMDGPU::S_SET_GPR_IDX_ON)
236         continue;
237 
238       Count = 0;
239       if (!SetGPRMI) {
240         SetGPRMI = &MI;
241         continue;
242       }
243 
244       if (optimizeSetGPR(*SetGPRMI, MI))
245         Changed = true;
246       else
247         SetGPRMI = &MI;
248     }
249   }
250 
251   return Changed;
252 }
253