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