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