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
38using namespace llvm;
39
40#define DEBUG_TYPE "tfr-cleanup"
41
42namespace {
43class HexagonTfrCleanup : public MachineFunctionPass {
44public:
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
54private:
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
69char HexagonTfrCleanup::ID = 0;
70
71namespace llvm {
72char &HexagonTfrCleanupID = HexagonTfrCleanup::ID;
73}
74
75bool 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.
82void 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.
88bool 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.
115bool 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.
171bool 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.
222bool 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
261bool 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//===----------------------------------------------------------------------===//
300INITIALIZE_PASS(HexagonTfrCleanup, "tfr-cleanup", "Hexagon TFR Cleanup", false,
301 false)
302
303FunctionPass *llvm::createHexagonTfrCleanup() {
304 return new HexagonTfrCleanup();
305}
306