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