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 
34 public:
35   static char ID;
36 
37   SIPreEmitPeephole() : MachineFunctionPass(ID) {
38     initializeSIPreEmitPeepholePass(*PassRegistry::getPassRegistry());
39   }
40 
41   bool runOnMachineFunction(MachineFunction &MF) override;
42 };
43 
44 } // End anonymous namespace.
45 
46 INITIALIZE_PASS(SIPreEmitPeephole, DEBUG_TYPE,
47                 "SI peephole optimizations", false, false)
48 
49 char SIPreEmitPeephole::ID = 0;
50 
51 char &llvm::SIPreEmitPeepholeID = SIPreEmitPeephole::ID;
52 
53 bool SIPreEmitPeephole::optimizeVccBranch(MachineInstr &MI) const {
54   // Match:
55   // sreg = -1
56   // vcc = S_AND_B64 exec, sreg
57   // S_CBRANCH_VCC[N]Z
58   // =>
59   // S_CBRANCH_EXEC[N]Z
60   // We end up with this pattern sometimes after basic block placement.
61   // It happens while combining a block which assigns -1 to a saved mask and
62   // another block which consumes that saved mask and then a branch.
63   bool Changed = false;
64   MachineBasicBlock &MBB = *MI.getParent();
65   const GCNSubtarget &ST = MBB.getParent()->getSubtarget<GCNSubtarget>();
66   const bool IsWave32 = ST.isWave32();
67   const unsigned CondReg = TRI->getVCC();
68   const unsigned ExecReg = IsWave32 ? AMDGPU::EXEC_LO : AMDGPU::EXEC;
69   const unsigned And = IsWave32 ? AMDGPU::S_AND_B32 : AMDGPU::S_AND_B64;
70 
71   MachineBasicBlock::reverse_iterator A = MI.getReverseIterator(),
72                                       E = MBB.rend();
73   bool ReadsCond = false;
74   unsigned Threshold = 5;
75   for (++A; A != E; ++A) {
76     if (!--Threshold)
77       return false;
78     if (A->modifiesRegister(ExecReg, TRI))
79       return false;
80     if (A->modifiesRegister(CondReg, TRI)) {
81       if (!A->definesRegister(CondReg, TRI) || A->getOpcode() != And)
82         return false;
83       break;
84     }
85     ReadsCond |= A->readsRegister(CondReg, TRI);
86   }
87   if (A == E)
88     return false;
89 
90   MachineOperand &Op1 = A->getOperand(1);
91   MachineOperand &Op2 = A->getOperand(2);
92   if (Op1.getReg() != ExecReg && Op2.isReg() && Op2.getReg() == ExecReg) {
93     TII->commuteInstruction(*A);
94     Changed = true;
95   }
96   if (Op1.getReg() != ExecReg)
97     return Changed;
98   if (Op2.isImm() && Op2.getImm() != -1)
99     return Changed;
100 
101   Register SReg;
102   if (Op2.isReg()) {
103     SReg = Op2.getReg();
104     auto M = std::next(A);
105     bool ReadsSreg = false;
106     for (; M != E; ++M) {
107       if (M->definesRegister(SReg, TRI))
108         break;
109       if (M->modifiesRegister(SReg, TRI))
110         return Changed;
111       ReadsSreg |= M->readsRegister(SReg, TRI);
112     }
113     if (M == E || !M->isMoveImmediate() || !M->getOperand(1).isImm() ||
114         M->getOperand(1).getImm() != -1)
115       return Changed;
116     // First if sreg is only used in and instruction fold the immediate
117     // into that and.
118     if (!ReadsSreg && Op2.isKill()) {
119       A->getOperand(2).ChangeToImmediate(-1);
120       M->eraseFromParent();
121     }
122   }
123 
124   if (!ReadsCond && A->registerDefIsDead(AMDGPU::SCC) &&
125       MI.killsRegister(CondReg, TRI))
126     A->eraseFromParent();
127 
128   bool IsVCCZ = MI.getOpcode() == AMDGPU::S_CBRANCH_VCCZ;
129   if (SReg == ExecReg) {
130     if (IsVCCZ) {
131       MI.eraseFromParent();
132       return true;
133     }
134     MI.setDesc(TII->get(AMDGPU::S_BRANCH));
135   } else {
136     MI.setDesc(
137         TII->get(IsVCCZ ? AMDGPU::S_CBRANCH_EXECZ : AMDGPU::S_CBRANCH_EXECNZ));
138   }
139 
140   MI.RemoveOperand(MI.findRegisterUseOperandIdx(CondReg, false /*Kill*/, TRI));
141   MI.addImplicitDefUseOperands(*MBB.getParent());
142 
143   return true;
144 }
145 
146 bool SIPreEmitPeephole::runOnMachineFunction(MachineFunction &MF) {
147   const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
148   TII = ST.getInstrInfo();
149   TRI = &TII->getRegisterInfo();
150   bool Changed = false;
151 
152   for (MachineBasicBlock &MBB : MF) {
153     MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
154     if (MBBI == MBB.end())
155       continue;
156 
157     MachineInstr &MI = *MBBI;
158     switch (MI.getOpcode()) {
159     case AMDGPU::S_CBRANCH_VCCZ:
160     case AMDGPU::S_CBRANCH_VCCNZ:
161       Changed |= optimizeVccBranch(MI);
162       break;
163     default:
164       break;
165     }
166   }
167 
168   return Changed;
169 }
170