1//===-- HexagonPeephole.cpp - Hexagon Peephole Optimizations --------------===//
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// This peephole pass optimizes in the following cases.
8// 1. Optimizes redundant sign extends for the following case
9// Transform the following pattern
10// %170 = SXTW %166
11// ...
12// %176 = COPY %170:isub_lo
13//
14// Into
15// %176 = COPY %166
16//
17// 2. Optimizes redundant negation of predicates.
18// %15 = CMPGTrr %6, %2
19// ...
20// %16 = NOT_p killed %15
21// ...
22// JMP_c killed %16, <%bb.1>, implicit dead %pc
23//
24// Into
25// %15 = CMPGTrr %6, %2;
26// ...
27// JMP_cNot killed %15, <%bb.1>, implicit dead %pc;
28//
29// 3. Fuse the A2_vminub/C2_cmpgtup intrinsic pair, which share inputs, into the
30// dual-output A6_vminub_RdP hardware instruction.
31// %1 = A2_vminub %a, %b
32// %2 = C2_cmpgtup %a, %b
33// turning it into
34// %3, %4 = A6_vminub_RdP %a, %b
35// (Hexagon has no multi-output intrinsics, so the two results are produced by
36// separate intrinsics that this pass recombines.)
37//
38// Note: The first two transformations make instructions like
39// %170 = SXTW %166 or %16 = NOT_p killed %15
40// redundant. A dead-instruction removal pass, such as DCE or DIE, eliminates
41// them.
42
43//===----------------------------------------------------------------------===//
44
45#include "Hexagon.h"
46#include "HexagonTargetMachine.h"
47#include "llvm/ADT/ArrayRef.h"
48#include "llvm/ADT/DenseMap.h"
49#include "llvm/ADT/SmallPtrSet.h"
50#include "llvm/ADT/SmallVector.h"
51#include "llvm/ADT/Statistic.h"
52#include "llvm/CodeGen/MachineFunction.h"
53#include "llvm/CodeGen/MachineFunctionPass.h"
54#include "llvm/CodeGen/MachineInstrBuilder.h"
55#include "llvm/CodeGen/MachineRegisterInfo.h"
56#include "llvm/CodeGen/Passes.h"
57#include "llvm/CodeGen/TargetInstrInfo.h"
58#include "llvm/CodeGen/TargetRegisterInfo.h"
59#include "llvm/Pass.h"
60#include "llvm/Support/CommandLine.h"
61#include "llvm/Target/TargetMachine.h"
62
63using namespace llvm;
64
65#define DEBUG_TYPE "hexagon-peephole"
66
67static cl::opt<bool>
68 DisableHexagonPeephole("disable-hexagon-peephole", cl::Hidden,
69 cl::desc("Disable Peephole Optimization"));
70
71static cl::opt<bool> DisablePNotP("disable-hexagon-pnotp", cl::Hidden,
72 cl::desc("Disable Optimization of PNotP"));
73
74static cl::opt<bool>
75 DisableOptSZExt("disable-hexagon-optszext", cl::Hidden, cl::init(Val: true),
76 cl::desc("Disable Optimization of Sign/Zero Extends"));
77
78static cl::opt<bool>
79 DisableOptExtTo64("disable-hexagon-opt-ext-to-64", cl::Hidden,
80 cl::init(Val: true),
81 cl::desc("Disable Optimization of extensions to i64."));
82
83static cl::opt<bool>
84 FuseIntrinsicVMinUB("hexagon-fuse-intrinsic-vminub", cl::Hidden,
85 cl::init(Val: true),
86 cl::desc("Fuse A2_vminub/C2_cmpgtup into "
87 "A6_vminub_RdP."));
88
89namespace {
90 struct HexagonPeephole : public MachineFunctionPass {
91 const HexagonInstrInfo *QII;
92 MachineRegisterInfo *MRI;
93
94 public:
95 static char ID;
96 HexagonPeephole() : MachineFunctionPass(ID) {}
97
98 bool runOnMachineFunction(MachineFunction &MF) override;
99 bool fuseIntrinsicVMinUB(MachineFunction &MF);
100
101 StringRef getPassName() const override {
102 return "Hexagon optimize redundant zero and size extends";
103 }
104
105 void getAnalysisUsage(AnalysisUsage &AU) const override {
106 MachineFunctionPass::getAnalysisUsage(AU);
107 }
108 };
109}
110
111char HexagonPeephole::ID = 0;
112
113INITIALIZE_PASS(HexagonPeephole, "hexagon-peephole", "Hexagon Peephole",
114 false, false)
115
116bool HexagonPeephole::runOnMachineFunction(MachineFunction &MF) {
117 if (skipFunction(F: MF.getFunction()))
118 return false;
119
120 bool Changed = false;
121
122 QII = static_cast<const HexagonInstrInfo *>(MF.getSubtarget().getInstrInfo());
123 MRI = &MF.getRegInfo();
124
125 DenseMap<unsigned, unsigned> PeepholeMap;
126 DenseMap<unsigned, std::pair<unsigned, unsigned> > PeepholeDoubleRegsMap;
127
128 if (DisableHexagonPeephole) return false;
129
130 // Loop over all of the basic blocks.
131 for (MachineBasicBlock &MBB : MF) {
132 PeepholeMap.clear();
133 PeepholeDoubleRegsMap.clear();
134
135 // Traverse the basic block.
136 for (MachineInstr &MI : llvm::make_early_inc_range(Range&: MBB)) {
137 // Look for sign extends:
138 // %170 = SXTW %166
139 if (!DisableOptSZExt && MI.getOpcode() == Hexagon::A2_sxtw) {
140 assert(MI.getNumOperands() == 2);
141 MachineOperand &Dst = MI.getOperand(i: 0);
142 MachineOperand &Src = MI.getOperand(i: 1);
143 Register DstReg = Dst.getReg();
144 Register SrcReg = Src.getReg();
145 // Just handle virtual registers.
146 if (DstReg.isVirtual() && SrcReg.isVirtual()) {
147 // Map the following:
148 // %170 = SXTW %166
149 // PeepholeMap[170] = %166
150 PeepholeMap[DstReg] = SrcReg;
151 }
152 }
153
154 // Look for %170 = COMBINE_ir_V4 (0, %169)
155 // %170:DoublRegs, %169:IntRegs
156 if (!DisableOptExtTo64 && MI.getOpcode() == Hexagon::A4_combineir) {
157 assert(MI.getNumOperands() == 3);
158 MachineOperand &Dst = MI.getOperand(i: 0);
159 MachineOperand &Src1 = MI.getOperand(i: 1);
160 MachineOperand &Src2 = MI.getOperand(i: 2);
161 if (Src1.getImm() != 0)
162 continue;
163 Register DstReg = Dst.getReg();
164 Register SrcReg = Src2.getReg();
165 PeepholeMap[DstReg] = SrcReg;
166 }
167
168 // Look for this sequence below
169 // %DoubleReg1 = LSRd_ri %DoubleReg0, 32
170 // %IntReg = COPY %DoubleReg1:isub_lo.
171 // and convert into
172 // %IntReg = COPY %DoubleReg0:isub_hi.
173 if (MI.getOpcode() == Hexagon::S2_lsr_i_p) {
174 assert(MI.getNumOperands() == 3);
175 MachineOperand &Dst = MI.getOperand(i: 0);
176 MachineOperand &Src1 = MI.getOperand(i: 1);
177 MachineOperand &Src2 = MI.getOperand(i: 2);
178 if (Src2.getImm() != 32)
179 continue;
180 Register DstReg = Dst.getReg();
181 Register SrcReg = Src1.getReg();
182 PeepholeDoubleRegsMap[DstReg] =
183 std::make_pair(x&: *&SrcReg, y: Hexagon::isub_hi);
184 }
185
186 // Look for P=NOT(P).
187 if (!DisablePNotP && MI.getOpcode() == Hexagon::C2_not) {
188 assert(MI.getNumOperands() == 2);
189 MachineOperand &Dst = MI.getOperand(i: 0);
190 MachineOperand &Src = MI.getOperand(i: 1);
191 Register DstReg = Dst.getReg();
192 Register SrcReg = Src.getReg();
193 // Just handle virtual registers.
194 if (DstReg.isVirtual() && SrcReg.isVirtual()) {
195 // Map the following:
196 // %170 = NOT_xx %166
197 // PeepholeMap[170] = %166
198 PeepholeMap[DstReg] = SrcReg;
199 }
200 }
201
202 // Look for copy:
203 // %176 = COPY %170:isub_lo
204 if (!DisableOptSZExt && MI.isCopy()) {
205 assert(MI.getNumOperands() == 2);
206 MachineOperand &Dst = MI.getOperand(i: 0);
207 MachineOperand &Src = MI.getOperand(i: 1);
208
209 // Make sure we are copying the lower 32 bits.
210 if (Src.getSubReg() != Hexagon::isub_lo)
211 continue;
212
213 Register DstReg = Dst.getReg();
214 Register SrcReg = Src.getReg();
215 if (DstReg.isVirtual() && SrcReg.isVirtual()) {
216 // Try to find in the map.
217 if (unsigned PeepholeSrc = PeepholeMap.lookup(Val: SrcReg)) {
218 // Change the 1st operand.
219 MI.removeOperand(OpNo: 1);
220 MI.addOperand(Op: MachineOperand::CreateReg(Reg: PeepholeSrc, isDef: false));
221 Changed = true;
222 } else {
223 DenseMap<unsigned, std::pair<unsigned, unsigned> >::iterator DI =
224 PeepholeDoubleRegsMap.find(Val: SrcReg);
225 if (DI != PeepholeDoubleRegsMap.end()) {
226 std::pair<unsigned,unsigned> PeepholeSrc = DI->second;
227 MI.removeOperand(OpNo: 1);
228 MI.addOperand(Op: MachineOperand::CreateReg(
229 Reg: PeepholeSrc.first, isDef: false /*isDef*/, isImp: false /*isImp*/,
230 isKill: false /*isKill*/, isDead: false /*isDead*/, isUndef: false /*isUndef*/,
231 isEarlyClobber: false /*isEarlyClobber*/, SubReg: PeepholeSrc.second));
232 Changed = true;
233 }
234 }
235 }
236 }
237
238 // Look for Predicated instructions.
239 if (!DisablePNotP) {
240 bool Done = false;
241 if (QII->isPredicated(MI)) {
242 MachineOperand &Op0 = MI.getOperand(i: 0);
243 Register Reg0 = Op0.getReg();
244 const TargetRegisterClass *RC0 = MRI->getRegClass(Reg: Reg0);
245 if (RC0->getID() == Hexagon::PredRegsRegClassID) {
246 // Handle instructions that have a prediate register in op0
247 // (most cases of predicable instructions).
248 if (Reg0.isVirtual()) {
249 // Try to find in the map.
250 if (unsigned PeepholeSrc = PeepholeMap.lookup(Val: Reg0)) {
251 // Change the 1st operand and, flip the opcode.
252 MI.getOperand(i: 0).setReg(PeepholeSrc);
253 MRI->clearKillFlags(Reg: PeepholeSrc);
254 int NewOp = QII->getInvertedPredicatedOpcode(Opc: MI.getOpcode());
255 MI.setDesc(QII->get(Opcode: NewOp));
256 Changed = true;
257 Done = true;
258 }
259 }
260 }
261 }
262
263 if (!Done) {
264 // Handle special instructions.
265 unsigned Op = MI.getOpcode();
266 unsigned NewOp = 0;
267 unsigned PR = 1, S1 = 2, S2 = 3; // Operand indices.
268
269 switch (Op) {
270 case Hexagon::C2_mux:
271 case Hexagon::C2_muxii:
272 NewOp = Op;
273 break;
274 case Hexagon::C2_muxri:
275 NewOp = Hexagon::C2_muxir;
276 break;
277 case Hexagon::C2_muxir:
278 NewOp = Hexagon::C2_muxri;
279 break;
280 }
281 if (NewOp) {
282 Register PSrc = MI.getOperand(i: PR).getReg();
283 if (unsigned POrig = PeepholeMap.lookup(Val: PSrc)) {
284 BuildMI(BB&: MBB, I: MI.getIterator(), MIMD: MI.getDebugLoc(), MCID: QII->get(Opcode: NewOp),
285 DestReg: MI.getOperand(i: 0).getReg())
286 .addReg(RegNo: POrig)
287 .add(MO: MI.getOperand(i: S2))
288 .add(MO: MI.getOperand(i: S1));
289 MRI->clearKillFlags(Reg: POrig);
290 MI.eraseFromParent();
291 Changed = true;
292 }
293 } // if (NewOp)
294 } // if (!Done)
295
296 } // if (!DisablePNotP)
297
298 } // Instruction
299 } // Basic Block
300
301 if (FuseIntrinsicVMinUB)
302 Changed |= fuseIntrinsicVMinUB(MF);
303
304 return Changed;
305}
306
307// Return true if both instructions have identical input operands in order.
308static bool hasCommonInputOps(const MachineInstr &I1, const MachineInstr &I2) {
309 auto I1Uses = I1.explicit_uses();
310 auto I2Uses = I2.explicit_uses();
311 unsigned I1NumUses = I1.getNumExplicitOperands() - I1.getNumExplicitDefs();
312 unsigned I2NumUses = I2.getNumExplicitOperands() - I2.getNumExplicitDefs();
313 if (!I1NumUses || I1NumUses != I2NumUses)
314 return false;
315
316 auto I2Use = I2Uses.begin();
317 for (const MachineOperand &I1Use : I1Uses)
318 if (!I1Use.isIdenticalTo(Other: *I2Use++))
319 return false;
320 return true;
321}
322
323static bool modifiesPhysicalInput(ArrayRef<Register> Inputs,
324 const MachineInstr &Between,
325 const TargetRegisterInfo *TRI) {
326 for (Register Input : Inputs)
327 if (Between.modifiesRegister(Reg: Input, TRI))
328 return true;
329 return false;
330}
331
332bool HexagonPeephole::fuseIntrinsicVMinUB(MachineFunction &MF) {
333 bool Changed = false;
334 const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
335
336 for (MachineBasicBlock &MBB : MF) {
337 SmallPtrSet<MachineInstr *, 8> DeadMIs;
338
339 for (MachineInstr &MI : MBB) {
340 unsigned Opc = MI.getOpcode();
341 if ((Opc != Hexagon::A2_vminub && Opc != Hexagon::C2_cmpgtup) ||
342 DeadMIs.count(Ptr: &MI))
343 continue;
344
345 unsigned SiblingOpc =
346 Opc == Hexagon::A2_vminub ? Hexagon::C2_cmpgtup : Hexagon::A2_vminub;
347 SmallVector<Register, 2> PhysicalInputs;
348 for (const MachineOperand &MO : MI.operands())
349 if (MO.isReg() && !MO.isDef() && MO.getReg().isPhysical())
350 PhysicalInputs.push_back(Elt: MO.getReg());
351 MachineInstr *Sibling = nullptr;
352 auto It = MI.getIterator();
353 for (++It; It != MBB.end(); ++It) {
354 if (!DeadMIs.count(Ptr: &*It) && It->getOpcode() == SiblingOpc &&
355 hasCommonInputOps(I1: MI, I2: *It)) {
356 Sibling = &*It;
357 break;
358 }
359 if (modifiesPhysicalInput(Inputs: PhysicalInputs, Between: *It, TRI))
360 break;
361 }
362 if (!Sibling)
363 continue;
364
365 MachineInstr *VMin = Opc == Hexagon::A2_vminub ? &MI : Sibling;
366 MachineInstr *Cmp = Opc == Hexagon::A2_vminub ? Sibling : &MI;
367 Register VMinReg =
368 MRI->createVirtualRegister(RegClass: &Hexagon::DoubleRegsRegClass);
369 Register CmpReg = MRI->createVirtualRegister(RegClass: &Hexagon::PredRegsRegClass);
370
371 BuildMI(BB&: MBB, I: MI.getIterator(), MIMD: MI.getDebugLoc(),
372 MCID: QII->get(Opcode: Hexagon::A6_vminub_RdP), DestReg: VMinReg)
373 .addReg(RegNo: CmpReg, Flags: RegState::Define)
374 .add(MO: VMin->getOperand(i: 1))
375 .add(MO: VMin->getOperand(i: 2));
376
377 Register VMinDef = VMin->getOperand(i: 0).getReg();
378 Register CmpDef = Cmp->getOperand(i: 0).getReg();
379 assert(VMinDef.isVirtual() && CmpDef.isVirtual() &&
380 "vminub fusion requires virtual result registers");
381 MRI->replaceRegWith(FromReg: VMinDef, ToReg: VMinReg);
382 VMin->getOperand(i: 0).setReg(VMinDef);
383 MRI->replaceRegWith(FromReg: CmpDef, ToReg: CmpReg);
384 Cmp->getOperand(i: 0).setReg(CmpDef);
385
386 DeadMIs.insert(Ptr: &MI);
387 DeadMIs.insert(Ptr: Sibling);
388 Changed = true;
389 }
390
391 for (MachineInstr *MI : DeadMIs)
392 MI->eraseFromParent();
393 }
394
395 return Changed;
396}
397
398FunctionPass *llvm::createHexagonPeephole() {
399 return new HexagonPeephole();
400}
401