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 | |