| 1 | //===------- HexagonTfrCleanup.cpp - Hexagon Transfer Cleanup Pass -------===// |
| 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 | // This pass is to address a situation that appears after register allocation |
| 9 | // every now and then, namely a register copy from a source that was defined |
| 10 | // as an immediate value in the same block (usually just before the copy). |
| 11 | // |
| 12 | // Here is an example of actual code emitted that shows this problem: |
| 13 | // |
| 14 | // .LBB0_5: |
| 15 | // { |
| 16 | // r5 = zxtb(r8) |
| 17 | // r6 = or(r6, ##12345) |
| 18 | // } |
| 19 | // { |
| 20 | // r3 = xor(r1, r2) |
| 21 | // r1 = #0 <-- r1 set to #0 |
| 22 | // } |
| 23 | // { |
| 24 | // r7 = r1 <-- r7 set to r1 |
| 25 | // r0 = zxtb(r3) |
| 26 | // } |
| 27 | |
| 28 | #include "Hexagon.h" |
| 29 | #include "HexagonTargetMachine.h" |
| 30 | |
| 31 | #include "llvm/CodeGen/LiveIntervals.h" |
| 32 | #include "llvm/CodeGen/MachineFunction.h" |
| 33 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
| 34 | #include "llvm/CodeGen/Passes.h" |
| 35 | #include "llvm/CodeGen/TargetInstrInfo.h" |
| 36 | #include "llvm/CodeGen/TargetRegisterInfo.h" |
| 37 | |
| 38 | using namespace llvm; |
| 39 | |
| 40 | #define DEBUG_TYPE "tfr-cleanup" |
| 41 | |
| 42 | namespace { |
| 43 | class HexagonTfrCleanup : public MachineFunctionPass { |
| 44 | public: |
| 45 | static char ID; |
| 46 | HexagonTfrCleanup() : MachineFunctionPass(ID), HII(0), TRI(0) {} |
| 47 | StringRef getPassName() const override { return "Hexagon TFR Cleanup" ; } |
| 48 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 49 | AU.setPreservesAll(); |
| 50 | MachineFunctionPass::getAnalysisUsage(AU); |
| 51 | } |
| 52 | bool runOnMachineFunction(MachineFunction &MF) override; |
| 53 | |
| 54 | private: |
| 55 | const HexagonInstrInfo *HII; |
| 56 | const TargetRegisterInfo *TRI; |
| 57 | |
| 58 | typedef DenseMap<unsigned, uint64_t> ImmediateMap; |
| 59 | |
| 60 | bool isIntReg(unsigned Reg, bool &Is32); |
| 61 | void setReg(unsigned R32, uint32_t V32, ImmediateMap &IMap); |
| 62 | bool getReg(unsigned Reg, uint64_t &Val, ImmediateMap &IMap); |
| 63 | bool updateImmMap(MachineInstr *MI, ImmediateMap &IMap); |
| 64 | bool rewriteIfImm(MachineInstr *MI, ImmediateMap &IMap, SlotIndexes *Indexes); |
| 65 | bool eraseIfRedundant(MachineInstr *MI, SlotIndexes *Indexes); |
| 66 | }; |
| 67 | } // namespace |
| 68 | |
| 69 | char HexagonTfrCleanup::ID = 0; |
| 70 | |
| 71 | namespace llvm { |
| 72 | char &HexagonTfrCleanupID = HexagonTfrCleanup::ID; |
| 73 | } |
| 74 | |
| 75 | bool HexagonTfrCleanup::isIntReg(unsigned Reg, bool &Is32) { |
| 76 | Is32 = Hexagon::IntRegsRegClass.contains(Reg); |
| 77 | return Is32 || Hexagon::DoubleRegsRegClass.contains(Reg); |
| 78 | } |
| 79 | |
| 80 | // Assign given value V32 to the specified the register R32 in the map. Only |
| 81 | // 32-bit registers are valid arguments. |
| 82 | void HexagonTfrCleanup::setReg(unsigned R32, uint32_t V32, ImmediateMap &IMap) { |
| 83 | IMap[R32] = V32; |
| 84 | } |
| 85 | |
| 86 | // Retrieve a value of the provided register Reg and store it into Val. |
| 87 | // Return "true" if a value was found, "false" otherwise. |
| 88 | bool HexagonTfrCleanup::getReg(unsigned Reg, uint64_t &Val, |
| 89 | ImmediateMap &IMap) { |
| 90 | bool Is32; |
| 91 | if (!isIntReg(Reg, Is32)) |
| 92 | return false; |
| 93 | |
| 94 | if (Is32) { |
| 95 | ImmediateMap::iterator F = IMap.find(Val: Reg); |
| 96 | if (F == IMap.end()) |
| 97 | return false; |
| 98 | Val = F->second; |
| 99 | return true; |
| 100 | } |
| 101 | |
| 102 | // For 64-bit registers, compose the value from the values of its |
| 103 | // subregisters. |
| 104 | unsigned SubL = TRI->getSubReg(Reg, Idx: Hexagon::isub_lo); |
| 105 | unsigned SubH = TRI->getSubReg(Reg, Idx: Hexagon::isub_hi); |
| 106 | ImmediateMap::iterator FL = IMap.find(Val: SubL), FH = IMap.find(Val: SubH); |
| 107 | if (FL == IMap.end() || FH == IMap.end()) |
| 108 | return false; |
| 109 | Val = (FH->second << 32) | FL->second; |
| 110 | return true; |
| 111 | } |
| 112 | |
| 113 | // Process an instruction and record the relevant information in the imme- |
| 114 | // diate map. |
| 115 | bool HexagonTfrCleanup::updateImmMap(MachineInstr *MI, ImmediateMap &IMap) { |
| 116 | using namespace Hexagon; |
| 117 | |
| 118 | if (MI->isCall()) { |
| 119 | IMap.clear(); |
| 120 | return true; |
| 121 | } |
| 122 | |
| 123 | // If this is an instruction that loads a constant into a register, |
| 124 | // record this information in IMap. |
| 125 | unsigned Opc = MI->getOpcode(); |
| 126 | if (Opc == A2_tfrsi || Opc == A2_tfrpi) { |
| 127 | unsigned DefR = MI->getOperand(i: 0).getReg(); |
| 128 | bool Is32; |
| 129 | if (!isIntReg(Reg: DefR, Is32)) |
| 130 | return false; |
| 131 | if (!MI->getOperand(i: 1).isImm()) { |
| 132 | if (!Is32) { |
| 133 | IMap.erase(Val: TRI->getSubReg(Reg: DefR, Idx: isub_lo)); |
| 134 | IMap.erase(Val: TRI->getSubReg(Reg: DefR, Idx: isub_hi)); |
| 135 | } else { |
| 136 | IMap.erase(Val: DefR); |
| 137 | } |
| 138 | return false; |
| 139 | } |
| 140 | uint64_t Val = MI->getOperand(i: 1).getImm(); |
| 141 | // If it's a 64-bit register, break it up into subregisters. |
| 142 | if (!Is32) { |
| 143 | uint32_t VH = (Val >> 32), VL = (Val & 0xFFFFFFFFU); |
| 144 | setReg(R32: TRI->getSubReg(Reg: DefR, Idx: isub_lo), V32: VL, IMap); |
| 145 | setReg(R32: TRI->getSubReg(Reg: DefR, Idx: isub_hi), V32: VH, IMap); |
| 146 | } else { |
| 147 | setReg(R32: DefR, V32: Val, IMap); |
| 148 | } |
| 149 | return true; |
| 150 | } |
| 151 | |
| 152 | // Not a A2_tfr[sp]i. Invalidate all modified registers in IMap. |
| 153 | for (MachineInstr::mop_iterator Mo = MI->operands_begin(), |
| 154 | E = MI->operands_end(); |
| 155 | Mo != E; ++Mo) { |
| 156 | if (Mo->isRegMask()) { |
| 157 | IMap.clear(); |
| 158 | return true; |
| 159 | } |
| 160 | if (!Mo->isReg() || !Mo->isDef()) |
| 161 | continue; |
| 162 | unsigned R = Mo->getReg(); |
| 163 | for (MCRegAliasIterator AR(R, TRI, true); AR.isValid(); ++AR) |
| 164 | IMap.erase(Val: *AR); |
| 165 | } |
| 166 | return true; |
| 167 | } |
| 168 | |
| 169 | // Rewrite the instruction as A2_tfrsi/A2_tfrpi, it is a copy of a source that |
| 170 | // has a known constant value. |
| 171 | bool HexagonTfrCleanup::rewriteIfImm(MachineInstr *MI, ImmediateMap &IMap, |
| 172 | SlotIndexes *Indexes) { |
| 173 | using namespace Hexagon; |
| 174 | unsigned Opc = MI->getOpcode(); |
| 175 | switch (Opc) { |
| 176 | case A2_tfr: |
| 177 | case A2_tfrp: |
| 178 | case COPY: |
| 179 | break; |
| 180 | default: |
| 181 | return false; |
| 182 | } |
| 183 | |
| 184 | unsigned DstR = MI->getOperand(i: 0).getReg(); |
| 185 | unsigned SrcR = MI->getOperand(i: 1).getReg(); |
| 186 | bool Tmp, Is32; |
| 187 | if (!isIntReg(Reg: DstR, Is32) || !isIntReg(Reg: SrcR, Is32&: Tmp)) |
| 188 | return false; |
| 189 | assert(Tmp == Is32 && "Register size mismatch" ); |
| 190 | uint64_t Val; |
| 191 | bool Found = getReg(Reg: SrcR, Val, IMap); |
| 192 | if (!Found) |
| 193 | return false; |
| 194 | |
| 195 | MachineBasicBlock &B = *MI->getParent(); |
| 196 | DebugLoc DL = MI->getDebugLoc(); |
| 197 | int64_t SVal = Is32 ? int32_t(Val) : Val; |
| 198 | auto &HST = B.getParent()->getSubtarget<HexagonSubtarget>(); |
| 199 | MachineInstr *NewMI; |
| 200 | if (Is32) |
| 201 | NewMI = BuildMI(BB&: B, I: MI, MIMD: DL, MCID: HII->get(Opcode: A2_tfrsi), DestReg: DstR).addImm(Val: SVal); |
| 202 | else if (isInt<8>(x: SVal)) |
| 203 | NewMI = BuildMI(BB&: B, I: MI, MIMD: DL, MCID: HII->get(Opcode: A2_tfrpi), DestReg: DstR).addImm(Val: SVal); |
| 204 | else if (isInt<8>(x: SVal >> 32) && isInt<8>(x: int32_t(Val & 0xFFFFFFFFLL))) |
| 205 | NewMI = BuildMI(BB&: B, I: MI, MIMD: DL, MCID: HII->get(Opcode: A2_combineii), DestReg: DstR) |
| 206 | .addImm(Val: int32_t(SVal >> 32)) |
| 207 | .addImm(Val: int32_t(Val & 0xFFFFFFFFLL)); |
| 208 | else if (HST.isTinyCore()) |
| 209 | // Disable generating CONST64 since it requires load resource. |
| 210 | return false; |
| 211 | else |
| 212 | NewMI = BuildMI(BB&: B, I: MI, MIMD: DL, MCID: HII->get(Opcode: CONST64), DestReg: DstR).addImm(Val); |
| 213 | |
| 214 | // Replace the MI to reuse the same slot index |
| 215 | if (Indexes) |
| 216 | Indexes->replaceMachineInstrInMaps(MI&: *MI, NewMI&: *NewMI); |
| 217 | MI->eraseFromParent(); |
| 218 | return true; |
| 219 | } |
| 220 | |
| 221 | // Remove the instruction if it is a self-assignment. |
| 222 | bool HexagonTfrCleanup::eraseIfRedundant(MachineInstr *MI, |
| 223 | SlotIndexes *Indexes) { |
| 224 | unsigned Opc = MI->getOpcode(); |
| 225 | unsigned DefR, SrcR; |
| 226 | bool IsUndef = false; |
| 227 | switch (Opc) { |
| 228 | case Hexagon::A2_tfr: |
| 229 | // Rd = Rd |
| 230 | DefR = MI->getOperand(i: 0).getReg(); |
| 231 | SrcR = MI->getOperand(i: 1).getReg(); |
| 232 | IsUndef = MI->getOperand(i: 1).isUndef(); |
| 233 | break; |
| 234 | case Hexagon::A2_tfrt: |
| 235 | case Hexagon::A2_tfrf: |
| 236 | // if ([!]Pu) Rd = Rd |
| 237 | DefR = MI->getOperand(i: 0).getReg(); |
| 238 | SrcR = MI->getOperand(i: 2).getReg(); |
| 239 | IsUndef = MI->getOperand(i: 2).isUndef(); |
| 240 | break; |
| 241 | default: |
| 242 | return false; |
| 243 | } |
| 244 | if (DefR != SrcR) |
| 245 | return false; |
| 246 | if (IsUndef) { |
| 247 | MachineBasicBlock &B = *MI->getParent(); |
| 248 | DebugLoc DL = MI->getDebugLoc(); |
| 249 | auto DefI = BuildMI(BB&: B, I: MI, MIMD: DL, MCID: HII->get(Opcode: TargetOpcode::IMPLICIT_DEF), DestReg: DefR); |
| 250 | for (auto &Op : MI->operands()) |
| 251 | if (Op.isReg() && Op.isDef() && Op.isImplicit()) |
| 252 | DefI->addOperand(Op); |
| 253 | } |
| 254 | |
| 255 | if (Indexes) |
| 256 | Indexes->removeMachineInstrFromMaps(MI&: *MI); |
| 257 | MI->eraseFromParent(); |
| 258 | return true; |
| 259 | } |
| 260 | |
| 261 | bool HexagonTfrCleanup::runOnMachineFunction(MachineFunction &MF) { |
| 262 | bool Changed = false; |
| 263 | // Map: 32-bit register -> immediate value. |
| 264 | // 64-bit registers are stored through their subregisters. |
| 265 | ImmediateMap IMap; |
| 266 | auto *SIWrapper = getAnalysisIfAvailable<SlotIndexesWrapperPass>(); |
| 267 | SlotIndexes *Indexes = SIWrapper ? &SIWrapper->getSI() : nullptr; |
| 268 | |
| 269 | auto &HST = MF.getSubtarget<HexagonSubtarget>(); |
| 270 | HII = HST.getInstrInfo(); |
| 271 | TRI = HST.getRegisterInfo(); |
| 272 | |
| 273 | for (MachineBasicBlock &B : MF) { |
| 274 | MachineBasicBlock::iterator J, F, NextJ; |
| 275 | IMap.clear(); |
| 276 | bool Inserted = false, Erased = false; |
| 277 | for (J = B.begin(), F = B.end(); J != F; J = NextJ) { |
| 278 | NextJ = std::next(x: J); |
| 279 | MachineInstr *MI = &*J; |
| 280 | bool E = eraseIfRedundant(MI, Indexes); |
| 281 | Erased |= E; |
| 282 | if (E) |
| 283 | continue; |
| 284 | Inserted |= rewriteIfImm(MI, IMap, Indexes); |
| 285 | MachineBasicBlock::iterator NewJ = std::prev(x: NextJ); |
| 286 | updateImmMap(MI: &*NewJ, IMap); |
| 287 | } |
| 288 | bool BlockC = Inserted | Erased; |
| 289 | Changed |= BlockC; |
| 290 | if (BlockC && Indexes) |
| 291 | Indexes->repairIndexesInRange(MBB: &B, Begin: B.begin(), End: B.end()); |
| 292 | } |
| 293 | |
| 294 | return Changed; |
| 295 | } |
| 296 | |
| 297 | //===----------------------------------------------------------------------===// |
| 298 | // Public Constructor Functions |
| 299 | //===----------------------------------------------------------------------===// |
| 300 | INITIALIZE_PASS(HexagonTfrCleanup, "tfr-cleanup" , "Hexagon TFR Cleanup" , false, |
| 301 | false) |
| 302 | |
| 303 | FunctionPass *llvm::createHexagonTfrCleanup() { |
| 304 | return new HexagonTfrCleanup(); |
| 305 | } |
| 306 | |