1 | //===- CSEInfo.cpp ------------------------------===// |
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 | // |
9 | // |
10 | //===----------------------------------------------------------------------===// |
11 | #include "llvm/CodeGen/GlobalISel/CSEInfo.h" |
12 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
13 | #include "llvm/InitializePasses.h" |
14 | #include "llvm/Support/Error.h" |
15 | |
16 | #define DEBUG_TYPE "cseinfo" |
17 | |
18 | using namespace llvm; |
19 | char llvm::GISelCSEAnalysisWrapperPass::ID = 0; |
20 | GISelCSEAnalysisWrapperPass::GISelCSEAnalysisWrapperPass() |
21 | : MachineFunctionPass(ID) { |
22 | initializeGISelCSEAnalysisWrapperPassPass(*PassRegistry::getPassRegistry()); |
23 | } |
24 | INITIALIZE_PASS_BEGIN(GISelCSEAnalysisWrapperPass, DEBUG_TYPE, |
25 | "Analysis containing CSE Info" , false, true) |
26 | INITIALIZE_PASS_END(GISelCSEAnalysisWrapperPass, DEBUG_TYPE, |
27 | "Analysis containing CSE Info" , false, true) |
28 | |
29 | /// -------- UniqueMachineInstr -------------// |
30 | |
31 | void UniqueMachineInstr::Profile(FoldingSetNodeID &ID) { |
32 | GISelInstProfileBuilder(ID, MI->getMF()->getRegInfo()).addNodeID(MI); |
33 | } |
34 | /// ----------------------------------------- |
35 | |
36 | /// --------- CSEConfigFull ---------- /// |
37 | bool CSEConfigFull::shouldCSEOpc(unsigned Opc) { |
38 | switch (Opc) { |
39 | default: |
40 | break; |
41 | case TargetOpcode::G_ADD: |
42 | case TargetOpcode::G_AND: |
43 | case TargetOpcode::G_ASHR: |
44 | case TargetOpcode::G_LSHR: |
45 | case TargetOpcode::G_MUL: |
46 | case TargetOpcode::G_OR: |
47 | case TargetOpcode::G_SHL: |
48 | case TargetOpcode::G_SUB: |
49 | case TargetOpcode::G_XOR: |
50 | case TargetOpcode::G_UDIV: |
51 | case TargetOpcode::G_SDIV: |
52 | case TargetOpcode::G_UREM: |
53 | case TargetOpcode::G_SREM: |
54 | case TargetOpcode::G_CONSTANT: |
55 | case TargetOpcode::G_FCONSTANT: |
56 | case TargetOpcode::G_IMPLICIT_DEF: |
57 | case TargetOpcode::G_ZEXT: |
58 | case TargetOpcode::G_SEXT: |
59 | case TargetOpcode::G_ANYEXT: |
60 | case TargetOpcode::G_UNMERGE_VALUES: |
61 | case TargetOpcode::G_TRUNC: |
62 | case TargetOpcode::G_PTR_ADD: |
63 | case TargetOpcode::G_EXTRACT: |
64 | case TargetOpcode::G_SELECT: |
65 | case TargetOpcode::G_BUILD_VECTOR: |
66 | case TargetOpcode::G_BUILD_VECTOR_TRUNC: |
67 | case TargetOpcode::G_SEXT_INREG: |
68 | case TargetOpcode::G_FADD: |
69 | case TargetOpcode::G_FSUB: |
70 | case TargetOpcode::G_FMUL: |
71 | case TargetOpcode::G_FDIV: |
72 | case TargetOpcode::G_FABS: |
73 | // TODO: support G_FNEG. |
74 | case TargetOpcode::G_FMAXNUM: |
75 | case TargetOpcode::G_FMINNUM: |
76 | case TargetOpcode::G_FMAXNUM_IEEE: |
77 | case TargetOpcode::G_FMINNUM_IEEE: |
78 | return true; |
79 | } |
80 | return false; |
81 | } |
82 | |
83 | bool CSEConfigConstantOnly::shouldCSEOpc(unsigned Opc) { |
84 | return Opc == TargetOpcode::G_CONSTANT || Opc == TargetOpcode::G_FCONSTANT || |
85 | Opc == TargetOpcode::G_IMPLICIT_DEF; |
86 | } |
87 | |
88 | std::unique_ptr<CSEConfigBase> |
89 | llvm::getStandardCSEConfigForOpt(CodeGenOptLevel Level) { |
90 | std::unique_ptr<CSEConfigBase> Config; |
91 | if (Level == CodeGenOptLevel::None) |
92 | Config = std::make_unique<CSEConfigConstantOnly>(); |
93 | else |
94 | Config = std::make_unique<CSEConfigFull>(); |
95 | return Config; |
96 | } |
97 | |
98 | /// ----------------------------------------- |
99 | |
100 | /// -------- GISelCSEInfo -------------// |
101 | void GISelCSEInfo::setMF(MachineFunction &MF) { |
102 | this->MF = &MF; |
103 | this->MRI = &MF.getRegInfo(); |
104 | } |
105 | |
106 | GISelCSEInfo::~GISelCSEInfo() = default; |
107 | |
108 | bool GISelCSEInfo::isUniqueMachineInstValid( |
109 | const UniqueMachineInstr &UMI) const { |
110 | // Should we check here and assert that the instruction has been fully |
111 | // constructed? |
112 | // FIXME: Any other checks required to be done here? Remove this method if |
113 | // none. |
114 | return true; |
115 | } |
116 | |
117 | void GISelCSEInfo::invalidateUniqueMachineInstr(UniqueMachineInstr *UMI) { |
118 | bool Removed = CSEMap.RemoveNode(N: UMI); |
119 | (void)Removed; |
120 | assert(Removed && "Invalidation called on invalid UMI" ); |
121 | // FIXME: Should UMI be deallocated/destroyed? |
122 | } |
123 | |
124 | UniqueMachineInstr *GISelCSEInfo::getNodeIfExists(FoldingSetNodeID &ID, |
125 | MachineBasicBlock *MBB, |
126 | void *&InsertPos) { |
127 | auto *Node = CSEMap.FindNodeOrInsertPos(ID, InsertPos); |
128 | if (Node) { |
129 | if (!isUniqueMachineInstValid(UMI: *Node)) { |
130 | invalidateUniqueMachineInstr(UMI: Node); |
131 | return nullptr; |
132 | } |
133 | |
134 | if (Node->MI->getParent() != MBB) |
135 | return nullptr; |
136 | } |
137 | return Node; |
138 | } |
139 | |
140 | void GISelCSEInfo::insertNode(UniqueMachineInstr *UMI, void *InsertPos) { |
141 | handleRecordedInsts(); |
142 | assert(UMI); |
143 | UniqueMachineInstr *MaybeNewNode = UMI; |
144 | if (InsertPos) |
145 | CSEMap.InsertNode(N: UMI, InsertPos); |
146 | else |
147 | MaybeNewNode = CSEMap.GetOrInsertNode(N: UMI); |
148 | if (MaybeNewNode != UMI) { |
149 | // A similar node exists in the folding set. Let's ignore this one. |
150 | return; |
151 | } |
152 | assert(InstrMapping.count(UMI->MI) == 0 && |
153 | "This instruction should not be in the map" ); |
154 | InstrMapping[UMI->MI] = MaybeNewNode; |
155 | } |
156 | |
157 | UniqueMachineInstr *GISelCSEInfo::getUniqueInstrForMI(const MachineInstr *MI) { |
158 | assert(shouldCSE(MI->getOpcode()) && "Trying to CSE an unsupported Node" ); |
159 | auto *Node = new (UniqueInstrAllocator) UniqueMachineInstr(MI); |
160 | return Node; |
161 | } |
162 | |
163 | void GISelCSEInfo::insertInstr(MachineInstr *MI, void *InsertPos) { |
164 | assert(MI); |
165 | // If it exists in temporary insts, remove it. |
166 | TemporaryInsts.remove(I: MI); |
167 | auto *Node = getUniqueInstrForMI(MI); |
168 | insertNode(UMI: Node, InsertPos); |
169 | } |
170 | |
171 | MachineInstr *GISelCSEInfo::getMachineInstrIfExists(FoldingSetNodeID &ID, |
172 | MachineBasicBlock *MBB, |
173 | void *&InsertPos) { |
174 | handleRecordedInsts(); |
175 | if (auto *Inst = getNodeIfExists(ID, MBB, InsertPos)) { |
176 | LLVM_DEBUG(dbgs() << "CSEInfo::Found Instr " << *Inst->MI); |
177 | return const_cast<MachineInstr *>(Inst->MI); |
178 | } |
179 | return nullptr; |
180 | } |
181 | |
182 | void GISelCSEInfo::countOpcodeHit(unsigned Opc) { |
183 | #ifndef NDEBUG |
184 | ++OpcodeHitTable[Opc]; |
185 | #endif |
186 | // Else do nothing. |
187 | } |
188 | |
189 | void GISelCSEInfo::recordNewInstruction(MachineInstr *MI) { |
190 | if (shouldCSE(Opc: MI->getOpcode())) { |
191 | TemporaryInsts.insert(I: MI); |
192 | LLVM_DEBUG(dbgs() << "CSEInfo::Recording new MI " << *MI); |
193 | } |
194 | } |
195 | |
196 | void GISelCSEInfo::handleRecordedInst(MachineInstr *MI) { |
197 | assert(shouldCSE(MI->getOpcode()) && "Invalid instruction for CSE" ); |
198 | auto *UMI = InstrMapping.lookup(Val: MI); |
199 | LLVM_DEBUG(dbgs() << "CSEInfo::Handling recorded MI " << *MI); |
200 | if (UMI) { |
201 | // Invalidate this MI. |
202 | invalidateUniqueMachineInstr(UMI); |
203 | InstrMapping.erase(Val: MI); |
204 | } |
205 | /// Now insert the new instruction. |
206 | if (UMI) { |
207 | /// We'll reuse the same UniqueMachineInstr to avoid the new |
208 | /// allocation. |
209 | *UMI = UniqueMachineInstr(MI); |
210 | insertNode(UMI, InsertPos: nullptr); |
211 | } else { |
212 | /// This is a new instruction. Allocate a new UniqueMachineInstr and |
213 | /// Insert. |
214 | insertInstr(MI); |
215 | } |
216 | } |
217 | |
218 | void GISelCSEInfo::handleRemoveInst(MachineInstr *MI) { |
219 | if (auto *UMI = InstrMapping.lookup(Val: MI)) { |
220 | invalidateUniqueMachineInstr(UMI); |
221 | InstrMapping.erase(Val: MI); |
222 | } |
223 | TemporaryInsts.remove(I: MI); |
224 | } |
225 | |
226 | void GISelCSEInfo::handleRecordedInsts() { |
227 | if (HandlingRecordedInstrs) |
228 | return; |
229 | HandlingRecordedInstrs = true; |
230 | while (!TemporaryInsts.empty()) { |
231 | auto *MI = TemporaryInsts.pop_back_val(); |
232 | handleRecordedInst(MI); |
233 | } |
234 | HandlingRecordedInstrs = false; |
235 | } |
236 | |
237 | bool GISelCSEInfo::shouldCSE(unsigned Opc) const { |
238 | assert(CSEOpt.get() && "CSEConfig not set" ); |
239 | return CSEOpt->shouldCSEOpc(Opc); |
240 | } |
241 | |
242 | void GISelCSEInfo::erasingInstr(MachineInstr &MI) { handleRemoveInst(MI: &MI); } |
243 | void GISelCSEInfo::createdInstr(MachineInstr &MI) { recordNewInstruction(MI: &MI); } |
244 | void GISelCSEInfo::changingInstr(MachineInstr &MI) { |
245 | // For now, perform erase, followed by insert. |
246 | erasingInstr(MI); |
247 | createdInstr(MI); |
248 | } |
249 | void GISelCSEInfo::changedInstr(MachineInstr &MI) { changingInstr(MI); } |
250 | |
251 | void GISelCSEInfo::analyze(MachineFunction &MF) { |
252 | setMF(MF); |
253 | for (auto &MBB : MF) { |
254 | for (MachineInstr &MI : MBB) { |
255 | if (!shouldCSE(Opc: MI.getOpcode())) |
256 | continue; |
257 | LLVM_DEBUG(dbgs() << "CSEInfo::Add MI: " << MI); |
258 | insertInstr(MI: &MI); |
259 | } |
260 | } |
261 | } |
262 | |
263 | void GISelCSEInfo::releaseMemory() { |
264 | print(); |
265 | CSEMap.clear(); |
266 | InstrMapping.clear(); |
267 | UniqueInstrAllocator.Reset(); |
268 | TemporaryInsts.clear(); |
269 | CSEOpt.reset(); |
270 | MRI = nullptr; |
271 | MF = nullptr; |
272 | #ifndef NDEBUG |
273 | OpcodeHitTable.clear(); |
274 | #endif |
275 | } |
276 | |
277 | #ifndef NDEBUG |
278 | static const char *stringify(const MachineInstr *MI, std::string &S) { |
279 | raw_string_ostream OS(S); |
280 | OS << *MI; |
281 | return OS.str().c_str(); |
282 | } |
283 | #endif |
284 | |
285 | Error GISelCSEInfo::verify() { |
286 | #ifndef NDEBUG |
287 | std::string S1, S2; |
288 | handleRecordedInsts(); |
289 | // For each instruction in map from MI -> UMI, |
290 | // Profile(MI) and make sure UMI is found for that profile. |
291 | for (auto &It : InstrMapping) { |
292 | FoldingSetNodeID TmpID; |
293 | GISelInstProfileBuilder(TmpID, *MRI).addNodeID(It.first); |
294 | void *InsertPos; |
295 | UniqueMachineInstr *FoundNode = |
296 | CSEMap.FindNodeOrInsertPos(TmpID, InsertPos); |
297 | if (FoundNode != It.second) |
298 | return createStringError(std::errc::not_supported, |
299 | "CSEMap mismatch, InstrMapping has MIs without " |
300 | "corresponding Nodes in CSEMap:\n%s" , |
301 | stringify(It.second->MI, S1)); |
302 | } |
303 | |
304 | // For every node in the CSEMap, make sure that the InstrMapping |
305 | // points to it. |
306 | for (const UniqueMachineInstr &UMI : CSEMap) { |
307 | if (!InstrMapping.count(UMI.MI)) |
308 | return createStringError(std::errc::not_supported, |
309 | "Node in CSE without InstrMapping:\n%s" , |
310 | stringify(UMI.MI, S1)); |
311 | |
312 | if (InstrMapping[UMI.MI] != &UMI) |
313 | return createStringError(std::make_error_code(std::errc::not_supported), |
314 | "Mismatch in CSE mapping:\n%s\n%s" , |
315 | stringify(InstrMapping[UMI.MI]->MI, S1), |
316 | stringify(UMI.MI, S2)); |
317 | } |
318 | #endif |
319 | return Error::success(); |
320 | } |
321 | |
322 | void GISelCSEInfo::print() { |
323 | LLVM_DEBUG({ |
324 | for (auto &It : OpcodeHitTable) |
325 | dbgs() << "CSEInfo::CSE Hit for Opc " << It.first << " : " << It.second |
326 | << "\n" ; |
327 | }); |
328 | } |
329 | /// ----------------------------------------- |
330 | // ---- Profiling methods for FoldingSetNode --- // |
331 | const GISelInstProfileBuilder & |
332 | GISelInstProfileBuilder::addNodeID(const MachineInstr *MI) const { |
333 | addNodeIDMBB(MBB: MI->getParent()); |
334 | addNodeIDOpcode(Opc: MI->getOpcode()); |
335 | for (const auto &Op : MI->operands()) |
336 | addNodeIDMachineOperand(MO: Op); |
337 | addNodeIDFlag(Flag: MI->getFlags()); |
338 | return *this; |
339 | } |
340 | |
341 | const GISelInstProfileBuilder & |
342 | GISelInstProfileBuilder::addNodeIDOpcode(unsigned Opc) const { |
343 | ID.AddInteger(I: Opc); |
344 | return *this; |
345 | } |
346 | |
347 | const GISelInstProfileBuilder & |
348 | GISelInstProfileBuilder::addNodeIDRegType(const LLT Ty) const { |
349 | uint64_t Val = Ty.getUniqueRAWLLTData(); |
350 | ID.AddInteger(I: Val); |
351 | return *this; |
352 | } |
353 | |
354 | const GISelInstProfileBuilder & |
355 | GISelInstProfileBuilder::addNodeIDRegType(const TargetRegisterClass *RC) const { |
356 | ID.AddPointer(Ptr: RC); |
357 | return *this; |
358 | } |
359 | |
360 | const GISelInstProfileBuilder & |
361 | GISelInstProfileBuilder::addNodeIDRegType(const RegisterBank *RB) const { |
362 | ID.AddPointer(Ptr: RB); |
363 | return *this; |
364 | } |
365 | |
366 | const GISelInstProfileBuilder &GISelInstProfileBuilder::addNodeIDRegType( |
367 | MachineRegisterInfo::VRegAttrs Attrs) const { |
368 | addNodeIDRegType(Ty: Attrs.Ty); |
369 | |
370 | const RegClassOrRegBank &RCOrRB = Attrs.RCOrRB; |
371 | if (RCOrRB) { |
372 | if (const auto *RB = dyn_cast_if_present<const RegisterBank *>(Val: RCOrRB)) |
373 | addNodeIDRegType(RB); |
374 | else |
375 | addNodeIDRegType(RC: cast<const TargetRegisterClass *>(Val: RCOrRB)); |
376 | } |
377 | return *this; |
378 | } |
379 | |
380 | const GISelInstProfileBuilder & |
381 | GISelInstProfileBuilder::addNodeIDImmediate(int64_t Imm) const { |
382 | ID.AddInteger(I: Imm); |
383 | return *this; |
384 | } |
385 | |
386 | const GISelInstProfileBuilder & |
387 | GISelInstProfileBuilder::addNodeIDRegNum(Register Reg) const { |
388 | ID.AddInteger(I: Reg.id()); |
389 | return *this; |
390 | } |
391 | |
392 | const GISelInstProfileBuilder & |
393 | GISelInstProfileBuilder::addNodeIDRegType(const Register Reg) const { |
394 | addNodeIDMachineOperand(MO: MachineOperand::CreateReg(Reg, isDef: false)); |
395 | return *this; |
396 | } |
397 | |
398 | const GISelInstProfileBuilder & |
399 | GISelInstProfileBuilder::addNodeIDMBB(const MachineBasicBlock *MBB) const { |
400 | ID.AddPointer(Ptr: MBB); |
401 | return *this; |
402 | } |
403 | |
404 | const GISelInstProfileBuilder & |
405 | GISelInstProfileBuilder::addNodeIDFlag(unsigned Flag) const { |
406 | if (Flag) |
407 | ID.AddInteger(I: Flag); |
408 | return *this; |
409 | } |
410 | |
411 | const GISelInstProfileBuilder & |
412 | GISelInstProfileBuilder::addNodeIDReg(Register Reg) const { |
413 | addNodeIDRegType(Attrs: MRI.getVRegAttrs(Reg)); |
414 | return *this; |
415 | } |
416 | |
417 | const GISelInstProfileBuilder &GISelInstProfileBuilder::addNodeIDMachineOperand( |
418 | const MachineOperand &MO) const { |
419 | if (MO.isReg()) { |
420 | Register Reg = MO.getReg(); |
421 | if (!MO.isDef()) |
422 | addNodeIDRegNum(Reg); |
423 | |
424 | // Profile the register properties. |
425 | addNodeIDReg(Reg); |
426 | assert(!MO.isImplicit() && "Unhandled case" ); |
427 | } else if (MO.isImm()) |
428 | ID.AddInteger(I: MO.getImm()); |
429 | else if (MO.isCImm()) |
430 | ID.AddPointer(Ptr: MO.getCImm()); |
431 | else if (MO.isFPImm()) |
432 | ID.AddPointer(Ptr: MO.getFPImm()); |
433 | else if (MO.isPredicate()) |
434 | ID.AddInteger(I: MO.getPredicate()); |
435 | else |
436 | llvm_unreachable("Unhandled operand type" ); |
437 | // Handle other types |
438 | return *this; |
439 | } |
440 | |
441 | GISelCSEInfo & |
442 | GISelCSEAnalysisWrapper::get(std::unique_ptr<CSEConfigBase> CSEOpt, |
443 | bool Recompute) { |
444 | if (!AlreadyComputed || Recompute) { |
445 | Info.releaseMemory(); |
446 | Info.setCSEConfig(std::move(CSEOpt)); |
447 | Info.analyze(MF&: *MF); |
448 | AlreadyComputed = true; |
449 | } |
450 | return Info; |
451 | } |
452 | void GISelCSEAnalysisWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { |
453 | AU.setPreservesAll(); |
454 | MachineFunctionPass::getAnalysisUsage(AU); |
455 | } |
456 | |
457 | bool GISelCSEAnalysisWrapperPass::runOnMachineFunction(MachineFunction &MF) { |
458 | releaseMemory(); |
459 | Wrapper.setMF(MF); |
460 | return false; |
461 | } |
462 | |