| 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 | |
| 63 | using namespace llvm; |
| 64 | |
| 65 | #define DEBUG_TYPE "hexagon-peephole" |
| 66 | |
| 67 | static cl::opt<bool> |
| 68 | DisableHexagonPeephole("disable-hexagon-peephole" , cl::Hidden, |
| 69 | cl::desc("Disable Peephole Optimization" )); |
| 70 | |
| 71 | static cl::opt<bool> DisablePNotP("disable-hexagon-pnotp" , cl::Hidden, |
| 72 | cl::desc("Disable Optimization of PNotP" )); |
| 73 | |
| 74 | static cl::opt<bool> |
| 75 | DisableOptSZExt("disable-hexagon-optszext" , cl::Hidden, cl::init(Val: true), |
| 76 | cl::desc("Disable Optimization of Sign/Zero Extends" )); |
| 77 | |
| 78 | static 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 | |
| 83 | static 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 | |
| 89 | namespace { |
| 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 | |
| 111 | char HexagonPeephole::ID = 0; |
| 112 | |
| 113 | INITIALIZE_PASS(HexagonPeephole, "hexagon-peephole" , "Hexagon Peephole" , |
| 114 | false, false) |
| 115 | |
| 116 | bool 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. |
| 308 | static 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 | |
| 323 | static 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 | |
| 332 | bool 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 | |
| 398 | FunctionPass *llvm::createHexagonPeephole() { |
| 399 | return new HexagonPeephole(); |
| 400 | } |
| 401 | |