LLVM 24.0.0git
GCNVOPDUtils.cpp
Go to the documentation of this file.
1//===- GCNVOPDUtils.cpp - GCN VOPD Utils ------------------------===//
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/// \file This file contains the AMDGPU DAG scheduling
10/// mutation to pair VOPD instructions back to back. It also contains
11// subroutines useful in the creation of VOPD instructions
12//
13//===----------------------------------------------------------------------===//
14
15#include "GCNVOPDUtils.h"
16#include "AMDGPUSubtarget.h"
17#include "GCNSubtarget.h"
18#include "SIInstrInfo.h"
30#include "llvm/MC/MCInst.h"
32
33using namespace llvm;
34
35#define DEBUG_TYPE "gcn-vopd-utils"
36
37// Return the register class of the VOPDOpc operand named
38// src/vsrc<SrcIdx><CompIdx>, which is the slot src<SrcIdx> of MI<CompIdx> maps
39// to.
41 int VOPDOpc,
42 unsigned CompIdx,
43 unsigned SrcIdx) {
44 using namespace AMDGPU;
45 int OpIdx = -1;
46 const bool IsX = CompIdx == VOPD::X;
47 switch (SrcIdx) {
48 case 0:
49 OpIdx = getNamedOperandIdx(VOPDOpc, IsX ? OpName::src0X : OpName::src0Y);
50 break;
51 case 1:
52 OpIdx = getNamedOperandIdx(VOPDOpc, IsX ? OpName::vsrc1X : OpName::vsrc1Y);
53 break;
54 case 2:
55 OpIdx = getNamedOperandIdx(VOPDOpc, IsX ? OpName::vsrc2X : OpName::vsrc2Y);
56 if (OpIdx == -1)
57 OpIdx = getNamedOperandIdx(VOPDOpc, IsX ? OpName::src2X : OpName::src2Y);
58 break;
59 default:
60 llvm_unreachable("unexpected VOPD source index");
61 }
62
63 assert(OpIdx != -1);
64 return TII.getRegClass(TII.get(VOPDOpc), OpIdx);
65}
66
67// Check if physical register from src<SrcIdx> operand of MI<CompIdx> matches
68// register class constraints in corresponding VOPDOpc operand with name
69// src/vsrc<SrcIdx><CompIdx>.
70static bool isValidVOPDSrc(const SIInstrInfo &TII, int VOPDOpc,
71 unsigned CompIdx, unsigned SrcIdx,
72 Register PhysSrcReg) {
73 return getVOPDSrcRegClass(TII, VOPDOpc, CompIdx, SrcIdx)
74 ->contains(PhysSrcReg);
75}
76
78 AMDGPU::OpName Name) {
79 return MI.getOperand(getNamedOperandIdx(MI.getOpcode(), Name));
80}
81
82// Check if MI is a VOP3P instruction with operands that satisfy the constraints
83// for mapping it to a VOP2/VOPD opcode: no modifiers, no clamp, src1 and src2
84// are registers (src0 can be register or literal), and src2 is same as dst.
85// An immediate in src1 is accepted if src0 is a register and the operands can
86// be commuted. \p Commute is set if that is needed.
87static bool canMapVOP3PToVOPD(const SIInstrInfo &TII, const MachineInstr &MI,
88 bool &Commute) {
89 Commute = false;
90 unsigned Opc = MI.getOpcode();
91 if (Opc != AMDGPU::V_DOT2_F32_F16 && Opc != AMDGPU::V_DOT2_F32_BF16)
92 return false;
93 // src0 can be register or literal
94 if (getNamedOp(MI, AMDGPU::OpName::src0_modifiers).getImm() !=
96 return false;
97 if (getNamedOp(MI, AMDGPU::OpName::src1_modifiers).getImm() !=
99 return false;
100 int Src0Idx = getNamedOperandIdx(Opc, AMDGPU::OpName::src0);
101 int Src1Idx = getNamedOperandIdx(Opc, AMDGPU::OpName::src1);
102 const MachineOperand &Src0 = MI.getOperand(Src0Idx);
103 const MachineOperand &Src1 = MI.getOperand(Src1Idx);
104 // VOPD needs a register in src1. If src1 is an immediate, the dot2 can still
105 // be used once src0 and src1 are swapped.
106 if (!Src1.isReg()) {
107 if (!Src0.isReg() || !Src1.isImm() ||
108 !TII.isLegalToSwap(MI, Src0Idx, Src1Idx))
109 return false;
110 Commute = true;
111 }
112 if (getNamedOp(MI, AMDGPU::OpName::src2_modifiers).getImm() !=
114 return false;
115 if (!getNamedOp(MI, AMDGPU::OpName::src2).isReg())
116 return false;
117 if (getNamedOp(MI, AMDGPU::OpName::clamp).getImm() != 0)
118 return false;
119 return getNamedOp(MI, AMDGPU::OpName::vdst).getReg() ==
120 getNamedOp(MI, AMDGPU::OpName::src2).getReg();
121}
122
123// In a VOPD3 whose OPX is a 64-bit operation, an OPY VGPR source operand reads
124// back the wrong value if it is the last VGPR the wave owns.
125// A wave always owns a whole number of VGPR allocation granules, so only a
126// register just below a granule boundary can be the last one. The wave also
127// owns at least as many VGPRs as this function uses, so a source which has a
128// register above it in use here cannot be the last one either.
129// The number of VGPRs the wave is actually given is not available until the
130// assembler has seen the whole module, but that can only come out above this
131// function's own usage, so this is conservatively correct.
133 const MachineInstr &MIX,
134 const MachineInstr &MIY) {
135 const MachineFunction &MF = *MIX.getMF();
136 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
137 if (!ST.hasVOPD3F64OPYSrcHazard())
138 return false;
139
140 // Every 64-bit VOPD3 OPX opcode has a 64-bit vdst, and no 32-bit one has.
141 int VDstIdx =
142 AMDGPU::getNamedOperandIdx(MIX.getOpcode(), AMDGPU::OpName::vdst);
143 assert(VDstIdx != -1 && "VOPD3 OPX component must have a vdst");
144 if (TII.getOpSize(MIX, VDstIdx) != 8)
145 return false;
146
147 unsigned Granule =
148 AMDGPU::getVGPRAllocGranule(ST.getTargetID().getGPUKind(), ST.isWave32());
149 const SIRegisterInfo *TRI = ST.getRegisterInfo();
150 unsigned NumVGPRs = TRI->getNumUsedPhysRegs(
151 MF.getRegInfo(), AMDGPU::VGPR_32RegClass, /*IncludeCalls=*/false);
152 for (AMDGPU::OpName Name :
153 {AMDGPU::OpName::src0, AMDGPU::OpName::src1, AMDGPU::OpName::src2}) {
154 const MachineOperand *Src = TII.getNamedOperand(MIY, Name);
155 // Every OPY source which can be a VGPR is 32 bits wide.
156 if (!Src || !Src->isReg() ||
157 !AMDGPU::VGPR_32RegClass.contains(Src->getReg()))
158 continue;
159 unsigned Idx = TRI->getHWRegIndex(Src->getReg());
160 if ((Idx + 1) % Granule == 0 && Idx + 1 >= NumVGPRs)
161 return true;
162 }
163 return false;
164}
165
167 // A free register cannot be found without liveness. A move also makes the
168 // code longer, so a function which asked for small code keeps its literals.
169 return MF.getProperties().hasTracksLiveness() &&
170 !MF.getFunction().hasOptSize();
171}
172
174 const SIInstrInfo &TII, const MachineInstr &MIX, const MachineInstr &MIY,
175 bool IsVOPD3, bool AllowSameVGPR,
176 SmallVectorImpl<VOPDLiteralFixup> &LiteralFixups, bool (&CommuteXY)[2]) {
177 namespace VOPD = AMDGPU::VOPD;
178
179 const MachineFunction *MF = MIX.getMF();
180 const GCNSubtarget &ST = MF->getSubtarget<GCNSubtarget>();
181
182 if (IsVOPD3 && !ST.hasVOPD3())
183 return false;
184 if (IsVOPD3 && isVOPD3F64OPYSrcHazard(TII, MIX, MIY))
185 return false;
186 // Components which only map once src0 and src1 are commuted. The checks
187 // below look at their operands as they will be after the commute.
188 bool Commute[2] = {false, false};
189 if (!IsVOPD3 &&
190 ((TII.isVOP3(MIX) && !canMapVOP3PToVOPD(TII, MIX, Commute[VOPD::X])) ||
191 (TII.isVOP3(MIY) && !canMapVOP3PToVOPD(TII, MIY, Commute[VOPD::Y]))))
192 return false;
193 if (TII.isDPP(MIX) || TII.isDPP(MIY))
194 return false;
195
196 // Collected here and handed over only on success, so a failed check cannot
197 // leave anything behind.
199
200 const SIRegisterInfo *TRI = ST.getRegisterInfo();
201 const MachineRegisterInfo &MRI = MF->getRegInfo();
202 // Literals also count against scalar bus limit
204 auto AddLiteral = [&](const MachineOperand &Op) {
205 for (auto &Literal : UniqueLiterals) {
206 if (Literal->isIdenticalTo(Op))
207 return;
208 }
209 UniqueLiterals.push_back(&Op);
210 };
211 // Immediates which the caller will move into a scalar register. Identical
212 // values share one register, so they count like one scalar operand each.
213 SmallSet<int32_t, 2> MaterializedLiterals;
214 SmallSet<Register, 4> UniqueScalarRegs;
215
216 unsigned EncodingFamily = AMDGPU::getVOPDEncodingFamily(ST);
217 unsigned XOpc = AMDGPU::getVOPDOpcode(MIX.getOpcode(), IsVOPD3);
218 unsigned YOpc = AMDGPU::getVOPDOpcode(MIY.getOpcode(), IsVOPD3);
219 int VOPDOpc = AMDGPU::getVOPDFull(XOpc, YOpc, EncodingFamily, IsVOPD3);
220 assert(VOPDOpc != -1);
221
222 auto InstInfo = AMDGPU::getVOPDInstInfo(MIX.getDesc(), MIY.getDesc());
223
224 for (auto CompIdx : VOPD::COMPONENTS) {
225 const MachineInstr &MI = (CompIdx == VOPD::X) ? MIX : MIY;
226 const AMDGPU::OpName Src0Name =
227 Commute[CompIdx] ? AMDGPU::OpName::src1 : AMDGPU::OpName::src0;
228 const AMDGPU::OpName Src1Name =
229 Commute[CompIdx] ? AMDGPU::OpName::src0 : AMDGPU::OpName::src1;
230
231 const MachineOperand &Src0 = *TII.getNamedOperand(MI, Src0Name);
232 if (Src0.isReg()) {
233 if (!isValidVOPDSrc(TII, VOPDOpc, CompIdx, 0, Src0.getReg()))
234 return false;
235 if (TII.regUsesConstantBus(Src0, MRI))
236 UniqueScalarRegs.insert(Src0.getReg());
237 } else if (!TII.isInlineConstant(Src0)) {
238 if (!IsVOPD3) {
239 AddLiteral(Src0);
240 } else {
241 // A VOPD3 component cannot encode a literal, but src0 can read a
242 // scalar register. The pair is therefore still possible if the caller
243 // moves the value into one.
244 if (!canMaterializeVOPDLiterals(*MF) || !Src0.isImm())
245 return false;
246 // Only a 32-bit slot is handled, because the caller produces the value
247 // with a single S_MOV_B32.
248 int OpIdx = getNamedOperandIdx(MI.getOpcode(), Src0Name);
249 if (TII.getOpSize(MI, OpIdx) != 4)
250 return false;
251 const TargetRegisterClass *SlotRC =
252 TRI->getCommonSubClass(getVOPDSrcRegClass(TII, VOPDOpc, CompIdx, 0),
253 &AMDGPU::SGPR_32RegClass);
254 if (!SlotRC)
255 return false;
256 // Only the low bits reach the register, so two operands which name
257 // the same value share one move whichever way they were written.
258 int32_t Imm = static_cast<int32_t>(Src0.getImm());
259 MaterializedLiterals.insert(Imm);
260 Fixups.push_back({CompIdx, static_cast<unsigned>(OpIdx), Imm, SlotRC});
261 }
262 }
263
264 // V_FMAMK_F32 (src1) and V_FMAAK_F32 (src2) have a mandatory literal.
265 // VOPD3 instructions don't set MandatoryLiteralIdx.
266 if (InstInfo[CompIdx].hasMandatoryLiteral()) {
267 auto CompOprIdx = InstInfo[CompIdx].getMandatoryLiteralCompOperandIndex();
268 AddLiteral(MI.getOperand(CompOprIdx));
269 }
270
271 // VOPD only. Affects V_CNDMASK_B32_e32.
272 if (MI.getDesc().hasImplicitUseOfPhysReg(AMDGPU::VCC))
273 UniqueScalarRegs.insert(AMDGPU::VCC_LO);
274
275 if (const MachineOperand *Src1 = TII.getNamedOperand(MI, Src1Name)) {
276 if (Src1->isReg()) {
277 if (!isValidVOPDSrc(TII, VOPDOpc, CompIdx, 1, Src1->getReg()))
278 return false;
279 assert(TRI->isVectorRegister(MRI, Src1->getReg()));
280 } else if (IsVOPD3) {
281 return false;
282 }
283 }
284
285 if (IsVOPD3) {
286 if (const MachineOperand *Src2 =
287 TII.getNamedOperand(MI, AMDGPU::OpName::src2)) {
288 if (AMDGPU::hasNamedOperand(MI.getOpcode(), AMDGPU::OpName::bitop3)) {
289 // BITOP3 can be converted to DUAL_BITOP2 when src2 is zero.
290 if (!Src2->isImm() || Src2->getImm())
291 return false;
292 } else {
293 if (!Src2->isReg())
294 return false;
295 if (!isValidVOPDSrc(TII, VOPDOpc, CompIdx, 2, Src2->getReg()))
296 return false;
297 if (TII.regUsesConstantBus(*Src2, MRI)) {
298 assert(MI.getOpcode() == AMDGPU::V_CNDMASK_B32_e64);
299 UniqueScalarRegs.insert(Src2->getReg());
300 }
301 }
302 }
303 for (auto OpName : {AMDGPU::OpName::clamp, AMDGPU::OpName::omod,
304 AMDGPU::OpName::op_sel}) {
305 if (TII.hasModifiersSet(MI, OpName))
306 return false;
307 }
308
309 // Neg is allowed, other modifiers are not. NB: even though sext has the
310 // same value as neg, there are no combinable instructions with sext.
311 for (auto OpName :
312 {AMDGPU::OpName::src0_modifiers, AMDGPU::OpName::src1_modifiers,
313 AMDGPU::OpName::src2_modifiers}) {
314 const MachineOperand *Mods = TII.getNamedOperand(MI, OpName);
315 if (Mods && (Mods->getImm() & ~SISrcMods::NEG))
316 return false;
317 }
318 }
319 }
320
321 if (UniqueLiterals.size() > 1)
322 return false;
323 // Keep materialization pair-local and do not increase instruction count or
324 // register pressure by producing two different values for one pair.
325 if (MaterializedLiterals.size() > 1)
326 return false;
327 if ((UniqueLiterals.size() + MaterializedLiterals.size() +
328 UniqueScalarRegs.size()) > 2)
329 return false;
330
331 auto GetVRegIdx = [&](unsigned OpcodeIdx, unsigned OperandIdx) {
332 const MachineInstr &MI = (OpcodeIdx == VOPD::X) ? MIX : MIY;
333 if (Commute[OpcodeIdx]) {
334 int Src0Idx = getNamedOperandIdx(MI.getOpcode(), AMDGPU::OpName::src0);
335 int Src1Idx = getNamedOperandIdx(MI.getOpcode(), AMDGPU::OpName::src1);
336 if ((int)OperandIdx == Src0Idx)
337 OperandIdx = Src1Idx;
338 else if ((int)OperandIdx == Src1Idx)
339 OperandIdx = Src0Idx;
340 }
341 const MachineOperand &Operand = MI.getOperand(OperandIdx);
342 if (Operand.isReg() && TRI->isVectorRegister(MRI, Operand.getReg()))
343 return Operand.getReg();
344 return Register();
345 };
346
347 // On GFX1170+ if both OpX and OpY are V_MOV_B32 then OPY uses SRC2
348 // source-cache.
349 bool SkipSrc = (ST.hasGFX11_7Insts() || ST.hasGFX12Insts()) &&
350 MIX.getOpcode() == AMDGPU::V_MOV_B32_e32 &&
351 MIY.getOpcode() == AMDGPU::V_MOV_B32_e32;
352
353 // Check VGPR bank constraints for operand registers across both instructions.
354 if (InstInfo.hasInvalidOperand(GetVRegIdx, *TRI, SkipSrc, AllowSameVGPR,
355 IsVOPD3, ST.hasVOPDInterlockHazard()))
356 return false;
357
358 LLVM_DEBUG(dbgs() << "VOPD Reg Constraints Passed\n\tX: " << MIX
359 << "\n\tY: " << MIY << "\n");
360 LiteralFixups.assign(Fixups);
361 CommuteXY[VOPD::X] = Commute[VOPD::X];
362 CommuteXY[VOPD::Y] = Commute[VOPD::Y];
363 return true;
364}
365
366/// Core pair-eligibility check for a single VOPD encoding variant (VOPD or
367/// VOPD3). Returns the X/Y assignment on success, or std::nullopt otherwise.
368static std::optional<VOPDMatchInfo>
369tryMatchVOPDPairVariant(const SIInstrInfo &TII, unsigned EncodingFamily,
370 MachineInstr &FirstMI, MachineInstr &SecondMI,
371 bool IsVOPD3) {
372 unsigned Opc = FirstMI.getOpcode();
373 unsigned Opc2 = SecondMI.getOpcode();
374 AMDGPU::CanBeVOPD FirstCanBeVOPD =
375 AMDGPU::getCanBeVOPD(Opc, EncodingFamily, IsVOPD3);
376 AMDGPU::CanBeVOPD SecondCanBeVOPD =
377 AMDGPU::getCanBeVOPD(Opc2, EncodingFamily, IsVOPD3);
378
379 if (!(FirstCanBeVOPD.X && SecondCanBeVOPD.Y) &&
380 !(FirstCanBeVOPD.Y && SecondCanBeVOPD.X))
381 return std::nullopt;
382
383 // If SecondMI depends on FirstMI they cannot execute at the same time.
384 if (TII.hasRAWDependency(FirstMI, SecondMI))
385 return std::nullopt;
386
387 const GCNSubtarget &ST = TII.getSubtarget();
388 bool AllowSameVGPR = ST.hasGFX12Insts();
389
390 // Only a VOPD3 component can need a fixup; a plain one may hold a literal.
391 // checkVOPDRegConstraints() only writes these when it succeeds.
393 bool CommuteXY[2] = {false, false};
394
395 if (FirstCanBeVOPD.X && SecondCanBeVOPD.Y) {
396 if (checkVOPDRegConstraints(TII, FirstMI, SecondMI, IsVOPD3, AllowSameVGPR,
397 Fixups, CommuteXY))
398 return VOPDMatchInfo{
399 {&FirstMI, &SecondMI},
400 0,
401 IsVOPD3,
402 std::move(Fixups),
403 {CommuteXY[AMDGPU::VOPD::X], CommuteXY[AMDGPU::VOPD::Y]}};
404 }
405
406 if (FirstCanBeVOPD.Y && SecondCanBeVOPD.X) {
407 // AllowSameVGPR relaxes the VGPR bank overlap check for source operands.
408 // Only enable it when there is no antidependency.
409 bool IsAntiDep = TII.hasRAWDependency(SecondMI, FirstMI);
410 AllowSameVGPR &= !IsAntiDep;
411 if (IsAntiDep && !TII.isVOPDAntidependencyAllowed(SecondMI))
412 return std::nullopt;
413 if (checkVOPDRegConstraints(TII, SecondMI, FirstMI, IsVOPD3, AllowSameVGPR,
414 Fixups, CommuteXY))
415 return VOPDMatchInfo{
416 {&FirstMI, &SecondMI},
417 1,
418 IsVOPD3,
419 std::move(Fixups),
420 {CommuteXY[AMDGPU::VOPD::Y], CommuteXY[AMDGPU::VOPD::X]}};
421 }
422
423 return std::nullopt;
424}
425
426std::optional<VOPDMatchInfo> llvm::tryMatchVOPDPair(const SIInstrInfo &TII,
427 MachineInstr &FirstMI,
428 MachineInstr &SecondMI) {
429 const GCNSubtarget &ST = TII.getSubtarget();
430 unsigned EncodingFamily = AMDGPU::getVOPDEncodingFamily(ST);
431 if (auto Match = tryMatchVOPDPairVariant(TII, EncodingFamily, FirstMI,
432 SecondMI, /*IsVOPD3=*/false))
433 return Match;
434 if (ST.hasVOPD3())
435 return tryMatchVOPDPairVariant(TII, EncodingFamily, FirstMI, SecondMI,
436 /*IsVOPD3=*/true);
437 return std::nullopt;
438}
439
440/// Check if the instr pair, FirstMI and SecondMI, should be scheduled
441/// together. Given SecondMI, when FirstMI is unspecified, then check if
442/// SecondMI may be part of a fused pair at all.
444 const TargetSubtargetInfo &TSI,
445 const MachineInstr *FirstMI,
446 const MachineInstr &SecondMI,
447 const SDep *) {
448 const SIInstrInfo &STII = static_cast<const SIInstrInfo &>(TII);
449 const GCNSubtarget &ST = STII.getSubtarget();
450
451 // One instruction case: just check whether SecondMI is eligible at all.
452 if (!FirstMI) {
453 unsigned EncodingFamily = AMDGPU::getVOPDEncodingFamily(ST);
454 unsigned Opc2 = SecondMI.getOpcode();
455 auto CheckCanBeVOPD = [&](bool VOPD3) {
456 AMDGPU::CanBeVOPD CanBeVOPD =
457 AMDGPU::getCanBeVOPD(Opc2, EncodingFamily, VOPD3);
458 return CanBeVOPD.Y || CanBeVOPD.X;
459 };
460 return CheckCanBeVOPD(false) || (ST.hasVOPD3() && CheckCanBeVOPD(true));
461 }
462
463#ifdef EXPENSIVE_CHECKS
464 assert([&]() -> bool {
465 for (auto MII = MachineBasicBlock::const_iterator(FirstMI);
466 MII != FirstMI->getParent()->instr_end(); ++MII) {
467 if (&*MII == &SecondMI)
468 return true;
469 }
470 return false;
471 }() && "Expected FirstMI to precede SecondMI");
472#endif
473
474 return tryMatchVOPDPair(STII, *const_cast<MachineInstr *>(FirstMI),
475 const_cast<MachineInstr &>(SecondMI))
476 .has_value();
477}
478
479/// Collect all load (dependents if \p Forward else dependencies) that connect
480/// to the \p Head SU.
481/// \p Visited should allocate enough bits for the number of SUnits, but its
482/// value can otherwise be uninitialized.
483static void collectLoads(SmallPtrSet<SUnit *, 8> &Loads, BitVector &Visited,
484 SUnit &Head, bool Forward, bool StopAtLoads) {
485 if (Head.isBoundaryNode())
486 return;
487
488 Visited.reset();
489
491 Stack.push_back(&Head);
492 while (!Stack.empty()) {
493 SUnit *SU = Stack.pop_back_val();
494 const SmallVector<SDep, 4> &Deps = Forward ? SU->Succs : SU->Preds;
495 for (const SDep &Edge : Deps) {
496 if (StopAtLoads && Edge.getKind() != SDep::Data)
497 continue;
498 SUnit *Dep = Edge.getSUnit();
499 if (Dep->isBoundaryNode() || Visited.test(Dep->NodeNum))
500 continue;
501 Visited.set(Dep->NodeNum);
502
503 if (Dep->isInstr() && Dep->getInstr()->mayLoad()) {
504 Loads.insert(Dep);
505 if (StopAtLoads)
506 continue;
507 }
508 Stack.push_back(Dep);
509 }
510 }
511}
512
513/// Checks whether fusing SU \p I with SU \p J would force the loads preceding
514/// \p J to complete before loads depending on \p I.
515///
516/// \p ILoadSuccs should hold all first load successors of \p I (via
517/// collectLoads with StopAtLoads=true). For set bits in \p LoadPredsComputed,
518/// the corresponding set in \p LoadPredsCache should hold all transitive load
519/// dependencies (via collectLoads with StopAtLoads=false). The \p Scratch
520/// bitvector should allocate enough bits for the number of SUnits.
521static bool loadsMayOverlap(
522 [[maybe_unused]] SUnit &I, const SmallPtrSet<SUnit *, 8> &ILoadSuccs,
523 SUnit &J, BitVector &LoadPredsComputed,
524 SmallVector<SmallPtrSet<SUnit *, 8>> &LoadPredsCache, BitVector &Scratch) {
525
526 if (ILoadSuccs.empty())
527 return false;
528
529 SmallPtrSet<SUnit *, 8> &JLoadPreds = LoadPredsCache[J.NodeNum];
530 if (!LoadPredsComputed.test(J.NodeNum)) {
531 collectLoads(JLoadPreds, Scratch, J, /*Forward=*/false,
532 /*StopAtLoads=*/true);
533 LoadPredsComputed.set(J.NodeNum);
534 }
535 if (JLoadPreds.empty())
536 return false;
537
538 for (SUnit *ILoad : ILoadSuccs) {
539 SmallPtrSet<SUnit *, 8> &ILoadDeps = LoadPredsCache[ILoad->NodeNum];
540 if (!LoadPredsComputed.test(ILoad->NodeNum)) {
541 collectLoads(ILoadDeps, Scratch, *ILoad, /*Forward=*/false,
542 /*StopAtLoads=*/false);
543 LoadPredsComputed.set(ILoad->NodeNum);
544 }
545
546 for (SUnit *JLoad : JLoadPreds) {
547 if (ILoad == JLoad) {
549 dbgs() << "Will not pair " << I << " with " << J << "\n"
550 << " Fusion would introduce a cyclic dependency with "
551 << *ILoad << "\n");
552 return true;
553 }
554
555 if (!ILoadDeps.contains(JLoad)) {
556 LLVM_DEBUG(dbgs() << "Will not pair " << I << " with " << J << "\n"
557 << " Fusion may force " << *JLoad
558 << " to complete its load before dispatching "
559 << *ILoad << "\n");
560 return true;
561 }
562 }
563 }
564 return false;
565}
566
567namespace {
568/// Adapts design from MacroFusion
569/// Puts valid candidate instructions back-to-back so they can easily
570/// be turned into VOPD instructions
571/// Greedily pairs instruction candidates. O(n^2) algorithm.
572struct VOPDPairingMutation : ScheduleDAGMutation {
573 MacroFusionPredTy shouldScheduleAdjacent; // NOLINT: function pointer
574
575 VOPDPairingMutation(
576 MacroFusionPredTy shouldScheduleAdjacent) // NOLINT: function pointer
578
579 void apply(ScheduleDAGInstrs *DAG) override {
580 const TargetInstrInfo &TII = *DAG->TII;
581 const GCNSubtarget &ST = DAG->MF.getSubtarget<GCNSubtarget>();
582 if (!AMDGPU::hasVOPD(ST) || !ST.isWave32()) {
583 LLVM_DEBUG(dbgs() << "Target does not support VOPDPairingMutation\n");
584 return;
585 }
586
587 BitVector VOPDCapable(DAG->SUnits.size());
588 unsigned IIdx = 0;
589 // Pre-compute whether each individual instruction can be VOPD
590 for (auto ISUI = DAG->SUnits.begin(), E = DAG->SUnits.end(); ISUI != E;
591 ++ISUI, ++IIdx) {
592 const MachineInstr *IMI = ISUI->getInstr();
593 if (shouldScheduleAdjacent(TII, ST, nullptr, *IMI, nullptr) &&
594 hasLessThanNumFused(*ISUI, 2))
595 VOPDCapable[IIdx] = true;
596 }
597
598 IIdx = 0;
599 SmallPtrSet<SUnit *, 8> ILoadSuccs;
600
601 // Cache collected load predecessors.
602 // For VOPDCapable nodes, this caches collectLoads with StopAtLoads=true
603 // For loads, this caches collectLoads with StopAtLoads=false
604 BitVector LoadPredsComputed(DAG->SUnits.size());
605 SmallVector<SmallPtrSet<SUnit *, 8>> LoadPredsCache(DAG->SUnits.size());
606
607 BitVector Scratch(DAG->SUnits.size());
608 for (auto ISUI = DAG->SUnits.begin(), E = DAG->SUnits.end(); ISUI != E;
609 ++ISUI, ++IIdx) {
610 if (!VOPDCapable[IIdx])
611 continue;
612 const MachineInstr *IMI = ISUI->getInstr();
613
614 ILoadSuccs.clear();
615 collectLoads(ILoadSuccs, Scratch, *ISUI, /*Forward=*/true,
616 /*StopAtLoads=*/true);
617
618 unsigned JIdx = IIdx + 1;
619 for (auto JSUI = ISUI + 1; JSUI != E; ++JSUI, ++JIdx) {
620 if (!VOPDCapable[JIdx] || JSUI->isBoundaryNode())
621 continue;
622 const MachineInstr *JMI = JSUI->getInstr();
623 if (!hasLessThanNumFused(*JSUI, 2) ||
624 !shouldScheduleAdjacent(TII, ST, IMI, *JMI, nullptr))
625 continue;
626
627 if (loadsMayOverlap(*ISUI, ILoadSuccs, *JSUI, LoadPredsComputed,
628 LoadPredsCache, Scratch))
629 continue;
630
631 if (fuseInstructionPair(*DAG, *ISUI, *JSUI)) {
632 // Clear to prevent future checks/fusing
633 VOPDCapable[JIdx] = false;
634 break;
635 }
636 }
637 }
638 LLVM_DEBUG(dbgs() << "Completed VOPDPairingMutation\n");
639 }
640};
641} // namespace
642
643std::unique_ptr<ScheduleDAGMutation> llvm::createVOPDPairingMutation() {
644 return std::make_unique<VOPDPairingMutation>(shouldScheduleVOPDAdjacent);
645}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, const TargetSubtargetInfo &TSI, const MachineInstr *FirstMI, const MachineInstr &SecondMI, const SDep *Dep)
Check if the instr pair, FirstMI and SecondMI, should be fused together.
unsigned Imm
Base class for AMDGPU specific classes of TargetSubtarget.
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
AMD GCN specific subclass of TargetSubtarget.
static const MachineOperand & getNamedOp(const MachineInstr &MI, AMDGPU::OpName Name)
static void collectLoads(SmallPtrSet< SUnit *, 8 > &Loads, BitVector &Visited, SUnit &Head, bool Forward, bool StopAtLoads)
Collect all load (dependents if Forward else dependencies) that connect to the Head SU.
static bool checkVOPDRegConstraints(const SIInstrInfo &TII, const MachineInstr &MIX, const MachineInstr &MIY, bool IsVOPD3, bool AllowSameVGPR, SmallVectorImpl< VOPDLiteralFixup > &LiteralFixups, bool(&CommuteXY)[2])
static bool canMapVOP3PToVOPD(const SIInstrInfo &TII, const MachineInstr &MI, bool &Commute)
static const TargetRegisterClass * getVOPDSrcRegClass(const SIInstrInfo &TII, int VOPDOpc, unsigned CompIdx, unsigned SrcIdx)
static bool isVOPD3F64OPYSrcHazard(const SIInstrInfo &TII, const MachineInstr &MIX, const MachineInstr &MIY)
static bool canMaterializeVOPDLiterals(const MachineFunction &MF)
static std::optional< VOPDMatchInfo > tryMatchVOPDPairVariant(const SIInstrInfo &TII, unsigned EncodingFamily, MachineInstr &FirstMI, MachineInstr &SecondMI, bool IsVOPD3)
Core pair-eligibility check for a single VOPD encoding variant (VOPD or VOPD3).
static bool loadsMayOverlap(SUnit &I, const SmallPtrSet< SUnit *, 8 > &ILoadSuccs, SUnit &J, BitVector &LoadPredsComputed, SmallVector< SmallPtrSet< SUnit *, 8 > > &LoadPredsCache, BitVector &Scratch)
Checks whether fusing SU I with SU J would force the loads preceding J to complete before loads depen...
static bool shouldScheduleVOPDAdjacent(const TargetInstrInfo &TII, const TargetSubtargetInfo &TSI, const MachineInstr *FirstMI, const MachineInstr &SecondMI, const SDep *)
Check if the instr pair, FirstMI and SecondMI, should be scheduled together.
static bool isValidVOPDSrc(const SIInstrInfo &TII, int VOPDOpc, unsigned CompIdx, unsigned SrcIdx, Register PhysSrcReg)
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
static bool isReg(const MCInst &MI, unsigned OpNo)
Interface definition for SIInstrInfo.
This file defines the SmallVector class.
#define LLVM_DEBUG(...)
Definition Debug.h:119
bool test(unsigned Idx) const
Returns true if bit Idx is set.
Definition BitVector.h:482
BitVector & reset()
Reset all bits in the bitvector.
Definition BitVector.h:409
BitVector & set()
Set all bits in the bitvector.
Definition BitVector.h:366
bool hasOptSize() const
Optimize this function for size (-Os) or minimum size (-Oz).
Definition Function.h:699
bool contains(MCRegister Reg) const
contains - Return true if the specified register is included in this register class.
MachineInstrBundleIterator< const MachineInstr > const_iterator
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineFunctionProperties & getProperties() const
Get the function properties.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
bool mayLoad(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly read memory.
const MCInstrDesc & getDesc() const
Returns the target instruction descriptor of this MachineInstr.
LLVM_ABI const MachineFunction * getMF() const
Return the function that contains the basic block that this instruction belongs to.
MachineOperand class - Representation of each machine instruction operand.
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
Wrapper class representing virtual and physical registers.
Definition Register.h:20
Scheduling dependency.
Definition ScheduleDAG.h:54
@ Data
Regular data dependence (aka true-dependence).
Definition ScheduleDAG.h:58
const GCNSubtarget & getSubtarget() const
Scheduling unit. This is a node in the scheduling DAG.
bool isInstr() const
Returns true if this SUnit refers to a machine instruction as opposed to an SDNode.
unsigned NodeNum
Entry # of node in the node vector.
bool isBoundaryNode() const
Boundary nodes are placeholders for the boundary of the scheduling region.
SmallVector< SDep, 4 > Succs
All sunit successors.
SmallVector< SDep, 4 > Preds
All sunit predecessors.
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
const TargetInstrInfo * TII
Target instruction information.
std::vector< SUnit > SUnits
The scheduling units.
MachineFunction & MF
Machine function.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition SmallSet.h:184
size_type size() const
Definition SmallSet.h:171
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void assign(size_type NumElts, ValueParamT Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
TargetInstrInfo - Interface to description of machine instruction set.
TargetSubtargetInfo - Generic base class for all target subtargets.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned getVOPDOpcode(unsigned Opc, bool VOPD3)
CanBeVOPD getCanBeVOPD(unsigned Opc, unsigned EncodingFamily, bool VOPD3)
unsigned getVOPDEncodingFamily(const MCSubtargetInfo &ST)
LLVM_READONLY bool hasNamedOperand(uint32_t Opcode, OpName NamedIdx)
LLVM_ABI unsigned getVGPRAllocGranule(GPUKind AK, bool IsWave32)
VOPD::InstInfo getVOPDInstInfo(const MCInstrDesc &OpX, const MCInstrDesc &OpY)
bool hasVOPD(const MCSubtargetInfo &STI)
int getVOPDFull(unsigned OpX, unsigned OpY, unsigned EncodingFamily, bool VOPD3)
void apply(Opt *O, const Mod &M, const Mods &... Ms)
This is an optimization pass for GlobalISel generic memory operations.
std::unique_ptr< ScheduleDAGMutation > createVOPDPairingMutation()
LLVM_ABI bool fuseInstructionPair(ScheduleDAGInstrs &DAG, SUnit &FirstSU, SUnit &SecondSU)
Create an artificial edge between FirstSU and SecondSU.
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
DWARFExpression::Operation Op
std::optional< VOPDMatchInfo > tryMatchVOPDPair(const SIInstrInfo &TII, MachineInstr &FirstMI, MachineInstr &SecondMI)
Check whether FirstMI and SecondMI can be combined into a VOPD instruction.
bool(*)(const TargetInstrInfo &TII, const TargetSubtargetInfo &STI, const MachineInstr *FirstMI, const MachineInstr &SecondMI, const SDep *Dep) MacroFusionPredTy
Check if the instr pair, FirstMI and SecondMI, should be fused together, based on the dependency betw...
Definition MacroFusion.h:35
LLVM_ABI bool hasLessThanNumFused(const SUnit &SU, unsigned FuseLimit)
Checks if the number of cluster edges between SU and its predecessors is less than FuseLimit.
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
Describes a matched VOPD pair.