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
44using namespace llvm;
45
46namespace llvm {
47FunctionPass *createHexagonTfrCleanup();
48void initializeHexagonTfrCleanupPass(PassRegistry &);
49} // namespace llvm
50
51namespace {
52class HexagonTfrCleanup : public MachineFunctionPass {
53public:
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
66private:
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
81char HexagonTfrCleanup::ID = 0;
82
83namespace llvm {
84char &HexagonTfrCleanupID = HexagonTfrCleanup::ID;
85}
86
87bool 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.
94void 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.
104bool 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.
131bool 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.
190bool 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.
241bool 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
280bool 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//===----------------------------------------------------------------------===//
319INITIALIZE_PASS(HexagonTfrCleanup, "tfr-cleanup", "Hexagon TFR Cleanup", false,
320 false)
321
322FunctionPass *llvm::createHexagonTfrCleanup() {
323 return new HexagonTfrCleanup();
324}
325