LLVM 24.0.0git
AMDGPURegBankCombiner.cpp
Go to the documentation of this file.
1//=== lib/CodeGen/GlobalISel/AMDGPURegBankCombiner.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// This pass does combining of machine instructions at the generic MI level,
10// after register banks are known.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AMDGPU.h"
15#include "AMDGPULegalizerInfo.h"
17#include "GCNSubtarget.h"
29#include "llvm/IR/IntrinsicsAMDGPU.h"
31
32#define GET_GICOMBINER_DEPS
33#include "AMDGPUGenPreLegalizeGICombiner.inc"
34#undef GET_GICOMBINER_DEPS
35
36#define DEBUG_TYPE "amdgpu-regbank-combiner"
37
38using namespace llvm;
39using namespace MIPatternMatch;
40
41namespace {
42#define GET_GICOMBINER_TYPES
43#include "AMDGPUGenRegBankGICombiner.inc"
44#undef GET_GICOMBINER_TYPES
45
46class AMDGPURegBankCombinerImpl : public Combiner {
47protected:
48 const AMDGPURegBankCombinerImplRuleConfig &RuleConfig;
49 const GCNSubtarget &STI;
50 const RegisterBankInfo &RBI;
52 const SIInstrInfo &TII;
53 const CombinerHelper Helper;
54
55public:
56 AMDGPURegBankCombinerImpl(
58 GISelCSEInfo *CSEInfo,
59 const AMDGPURegBankCombinerImplRuleConfig &RuleConfig,
60 const GCNSubtarget &STI, MachineDominatorTree *MDT,
61 const LegalizerInfo *LI);
62
63 static const char *getName() { return "AMDGPURegBankCombinerImpl"; }
64
65 bool tryCombineAll(MachineInstr &I) const override;
66
67 bool isVgprRegBank(Register Reg) const;
68 Register getAsVgpr(Register Reg) const;
69
70 struct MinMaxMedOpc {
71 unsigned Min, Max, Med;
72 };
73
74 struct Med3MatchInfo {
75 unsigned Opc;
76 Register Val0, Val1, Val2;
77 };
78
79 struct MinMaxToMinMax3MatchInfo {
80 unsigned Opc;
81 Register Val0, Val1, Val2;
82 };
83
84 MinMaxMedOpc getMinMaxPair(unsigned Opc) const;
85
86 template <class m_Cst, typename CstTy>
87 bool matchMed(MachineInstr &MI, MachineRegisterInfo &MRI, MinMaxMedOpc MMMOpc,
88 Register &Val, CstTy &K0, CstTy &K1) const;
89
90 bool matchIntMinMaxToMed3(MachineInstr &MI, Med3MatchInfo &MatchInfo) const;
91 bool matchFPMinMaxToMed3(MachineInstr &MI, Med3MatchInfo &MatchInfo) const;
92 bool matchFPMinMaxToClamp(MachineInstr &MI, Register &Reg) const;
93 bool matchFPMed3ToClamp(MachineInstr &MI, Register &Reg) const;
94 void applyMed3(MachineInstr &MI, Med3MatchInfo &MatchInfo) const;
95 void applyClamp(MachineInstr &MI, Register &Reg) const;
96
97 void applyCanonicalizeZextShiftAmt(MachineInstr &MI, MachineInstr &Ext) const;
98
99 bool combineD16Load(MachineInstr &MI) const;
100 void applyD16Load(unsigned D16Opc, MachineInstr &DstMI,
101 MachineInstr *SmallLoad, Register ToOverwriteD16,
102 MachineInstr &InsertPt) const;
103
104 bool matchMinMaxToMinMax3(MachineInstr &MI,
105 MinMaxToMinMax3MatchInfo &MatchInfo) const;
106 void applyMinMaxToMinMax3(MachineInstr &MI,
107 MinMaxToMinMax3MatchInfo &MatchInfo) const;
108
109 bool matchCombine_mul24(MachineInstr &MI) const;
110 void applyCombine_mul24(MachineInstr &MI) const;
111
112private:
113 SIModeRegisterDefaults getMode() const;
114 bool getIEEE() const;
115 bool getDX10Clamp() const;
116 bool isFminnumIeee(const MachineInstr &MI) const;
117 bool isFminnum(const MachineInstr &MI) const;
118 bool isFminnumLike(const MachineInstr &MI) const;
119 bool isFCst(MachineInstr *MI) const;
120 bool isClampZeroToOne(MachineInstr *K0, MachineInstr *K1) const;
121
122#define GET_GICOMBINER_CLASS_MEMBERS
123#define AMDGPUSubtarget GCNSubtarget
124#include "AMDGPUGenRegBankGICombiner.inc"
125#undef GET_GICOMBINER_CLASS_MEMBERS
126#undef AMDGPUSubtarget
127};
128
129#define GET_GICOMBINER_IMPL
130#define AMDGPUSubtarget GCNSubtarget
131#include "AMDGPUGenRegBankGICombiner.inc"
132#undef AMDGPUSubtarget
133#undef GET_GICOMBINER_IMPL
134
135AMDGPURegBankCombinerImpl::AMDGPURegBankCombinerImpl(
137 GISelCSEInfo *CSEInfo,
138 const AMDGPURegBankCombinerImplRuleConfig &RuleConfig,
139 const GCNSubtarget &STI, MachineDominatorTree *MDT, const LegalizerInfo *LI)
140 : Combiner(MF, CInfo, &VT, CSEInfo), RuleConfig(RuleConfig), STI(STI),
141 RBI(*STI.getRegBankInfo()), TRI(*STI.getRegisterInfo()),
142 TII(*STI.getInstrInfo()),
143 Helper(Observer, B, /*IsPreLegalize*/ false, &VT, MDT, LI),
145#include "AMDGPUGenRegBankGICombiner.inc"
147{
148}
149
150bool AMDGPURegBankCombinerImpl::isVgprRegBank(Register Reg) const {
151 return RBI.getRegBank(Reg, MRI, TRI)->getID() == AMDGPU::VGPRRegBankID;
152}
153
154Register AMDGPURegBankCombinerImpl::getAsVgpr(Register Reg) const {
155 if (isVgprRegBank(Reg))
156 return Reg;
157
158 // Search for existing copy of Reg to vgpr.
159 for (MachineInstr &Use : MRI.use_instructions(Reg)) {
160 Register Def = Use.getOperand(0).getReg();
161 if (Use.getOpcode() == AMDGPU::COPY && isVgprRegBank(Def))
162 return Def;
163 }
164
165 // Copy Reg to vgpr.
166 Register VgprReg = B.buildCopy(MRI.getType(Reg), Reg).getReg(0);
167 MRI.setRegBank(VgprReg, RBI.getRegBank(AMDGPU::VGPRRegBankID));
168 return VgprReg;
169}
170
171AMDGPURegBankCombinerImpl::MinMaxMedOpc
172AMDGPURegBankCombinerImpl::getMinMaxPair(unsigned Opc) const {
173 switch (Opc) {
174 default:
175 llvm_unreachable("Unsupported opcode");
176 case AMDGPU::G_SMAX:
177 case AMDGPU::G_SMIN:
178 return {AMDGPU::G_SMIN, AMDGPU::G_SMAX, AMDGPU::G_AMDGPU_SMED3};
179 case AMDGPU::G_UMAX:
180 case AMDGPU::G_UMIN:
181 return {AMDGPU::G_UMIN, AMDGPU::G_UMAX, AMDGPU::G_AMDGPU_UMED3};
182 case AMDGPU::G_FMAXNUM:
183 case AMDGPU::G_FMINNUM:
184 return {AMDGPU::G_FMINNUM, AMDGPU::G_FMAXNUM, AMDGPU::G_AMDGPU_FMED3};
185 case AMDGPU::G_FMAXNUM_IEEE:
186 case AMDGPU::G_FMINNUM_IEEE:
187 return {AMDGPU::G_FMINNUM_IEEE, AMDGPU::G_FMAXNUM_IEEE,
188 AMDGPU::G_AMDGPU_FMED3};
189 }
190}
191
192template <class m_Cst, typename CstTy>
193bool AMDGPURegBankCombinerImpl::matchMed(MachineInstr &MI,
194 MachineRegisterInfo &MRI,
195 MinMaxMedOpc MMMOpc, Register &Val,
196 CstTy &K0, CstTy &K1) const {
197 // 4 operand commutes of: min(max(Val, K0), K1).
198 // Find K1 from outer instr: min(max(...), K1) or min(K1, max(...)).
199 // Find K0 and Val from inner instr: max(K0, Val) or max(Val, K0).
200 // 4 operand commutes of: max(min(Val, K1), K0).
201 // Find K0 from outer instr: max(min(...), K0) or max(K0, min(...)).
202 // Find K1 and Val from inner instr: min(K1, Val) or min(Val, K1).
203 return mi_match(
204 MI, MRI,
205 m_any_of(
207 MMMOpc.Min, m_CommutativeBinOp(MMMOpc.Max, m_Reg(Val), m_Cst(K0)),
208 m_Cst(K1)),
210 MMMOpc.Max, m_CommutativeBinOp(MMMOpc.Min, m_Reg(Val), m_Cst(K1)),
211 m_Cst(K0))));
212}
213
214bool AMDGPURegBankCombinerImpl::matchIntMinMaxToMed3(
215 MachineInstr &MI, Med3MatchInfo &MatchInfo) const {
216 Register Dst = MI.getOperand(0).getReg();
217 if (!isVgprRegBank(Dst))
218 return false;
219
220 // med3 for i16 is only available on gfx9+, and not available for v2i16.
221 LLT Ty = MRI.getType(Dst);
222 if ((Ty != LLT::scalar(16) || !STI.hasMed3_16()) && Ty != LLT::scalar(32))
223 return false;
224
225 MinMaxMedOpc OpcodeTriple = getMinMaxPair(MI.getOpcode());
226 Register Val;
227 std::optional<ValueAndVReg> K0, K1;
228 // Match min(max(Val, K0), K1) or max(min(Val, K1), K0). Then see if K0 <= K1.
229 if (!matchMed<GCstAndRegMatch>(MI, MRI, OpcodeTriple, Val, K0, K1))
230 return false;
231
232 if (OpcodeTriple.Med == AMDGPU::G_AMDGPU_SMED3 && K0->Value.sgt(K1->Value))
233 return false;
234 if (OpcodeTriple.Med == AMDGPU::G_AMDGPU_UMED3 && K0->Value.ugt(K1->Value))
235 return false;
236
237 MatchInfo = {OpcodeTriple.Med, Val, K0->VReg, K1->VReg};
238 return true;
239}
240
241// fmed3(NaN, K0, K1) = min(min(NaN, K0), K1)
242// ieee = true : min/max(SNaN, K) = QNaN, min/max(QNaN, K) = K
243// ieee = false : min/max(NaN, K) = K
244// clamp(NaN) = dx10_clamp ? 0.0 : NaN
245// Consider values of min(max(Val, K0), K1) and max(min(Val, K1), K0) as input.
246// Other operand commutes (see matchMed) give same result since min and max are
247// commutative.
248
249// Try to replace fp min(max(Val, K0), K1) or max(min(Val, K1), K0), KO<=K1
250// with fmed3(Val, K0, K1) or clamp(Val). Clamp requires K0 = 0.0 and K1 = 1.0.
251// Val = SNaN only for ieee = true
252// fmed3(SNaN, K0, K1) = min(min(SNaN, K0), K1) = min(QNaN, K1) = K1
253// min(max(SNaN, K0), K1) = min(QNaN, K1) = K1
254// max(min(SNaN, K1), K0) = max(K1, K0) = K1
255// Val = NaN,ieee = false or Val = QNaN,ieee = true
256// fmed3(NaN, K0, K1) = min(min(NaN, K0), K1) = min(K0, K1) = K0
257// min(max(NaN, K0), K1) = min(K0, K1) = K0 (can clamp when dx10_clamp = true)
258// max(min(NaN, K1), K0) = max(K1, K0) = K1 != K0
259bool AMDGPURegBankCombinerImpl::matchFPMinMaxToMed3(
260 MachineInstr &MI, Med3MatchInfo &MatchInfo) const {
261 Register Dst = MI.getOperand(0).getReg();
262 // Perform combine only when the destination is a VGPR.
263 if (!isVgprRegBank(Dst))
264 return false;
265
266 LLT Ty = MRI.getType(Dst);
267
268 // med3 for f16 is only available on gfx9+, and not available for v2f16.
269 if ((Ty != LLT::scalar(16) || !STI.hasMed3_16()) && Ty != LLT::scalar(32))
270 return false;
271
272 auto OpcodeTriple = getMinMaxPair(MI.getOpcode());
273
274 Register Val;
275 std::optional<FPValueAndVReg> K0, K1;
276 // Match min(max(Val, K0), K1) or max(min(Val, K1), K0). Then see if K0 <= K1.
277 if (!matchMed<GFCstAndRegMatch>(MI, MRI, OpcodeTriple, Val, K0, K1))
278 return false;
279
280 if (K0->Value > K1->Value)
281 return false;
282
283 // For IEEE=false perform combine only when it's safe to assume that there are
284 // no NaN inputs. Most often MI is marked with nnan fast math flag.
285 // For IEEE=true consider NaN inputs. fmed3(NaN, K0, K1) is equivalent to
286 // min(min(NaN, K0), K1). Safe to fold for min(max(Val, K0), K1) since inner
287 // nodes(max/min) have same behavior when one input is NaN and other isn't.
288 // Don't consider max(min(SNaN, K1), K0) since there is no isKnownNeverQNaN,
289 // also post-legalizer inputs to min/max are fcanonicalized (never SNaN).
290 if ((getIEEE() && isFminnumLike(MI)) || VT->isKnownNeverNaN(Dst)) {
291 // Don't fold single use constant that can't be inlined.
292 if ((!MRI.hasOneNonDBGUse(K0->VReg) || TII.isInlineConstant(K0->Value)) &&
293 (!MRI.hasOneNonDBGUse(K1->VReg) || TII.isInlineConstant(K1->Value))) {
294 MatchInfo = {OpcodeTriple.Med, Val, K0->VReg, K1->VReg};
295 return true;
296 }
297 }
298
299 return false;
300}
301
302bool AMDGPURegBankCombinerImpl::matchFPMinMaxToClamp(MachineInstr &MI,
303 Register &Reg) const {
304 // Perform combine only when the destination is a VGPR.
305 if (!isVgprRegBank(MI.getOperand(0).getReg()))
306 return false;
307
308 // Clamp is available on all types after regbankselect (f16, f32, f64, v2f16).
309 auto OpcodeTriple = getMinMaxPair(MI.getOpcode());
310 Register Val;
311 std::optional<FPValueAndVReg> K0, K1;
312 // Match min(max(Val, K0), K1) or max(min(Val, K1), K0).
313 if (!matchMed<GFCstOrSplatGFCstMatch>(MI, MRI, OpcodeTriple, Val, K0, K1))
314 return false;
315
316 if (!K0->Value.isPosZero() || !K1->Value.isOne())
317 return false;
318
319 // For IEEE=false perform combine only when it's safe to assume that there are
320 // no NaN inputs. Most often MI is marked with nnan fast math flag.
321 // For IEEE=true consider NaN inputs. Only min(max(QNaN, 0.0), 1.0) evaluates
322 // to 0.0 requires dx10_clamp = true.
323 if ((getIEEE() && getDX10Clamp() && isFminnumLike(MI) &&
324 VT->isKnownNeverSNaN(Val)) ||
325 VT->isKnownNeverNaN(MI.getOperand(0).getReg())) {
326 Reg = Val;
327 return true;
328 }
329
330 return false;
331}
332
333// Replacing fmed3(NaN, 0.0, 1.0) with clamp. Requires dx10_clamp = true.
334// Val = SNaN only for ieee = true. It is important which operand is NaN.
335// min(min(SNaN, 0.0), 1.0) = min(QNaN, 1.0) = 1.0
336// min(min(SNaN, 1.0), 0.0) = min(QNaN, 0.0) = 0.0
337// min(min(0.0, 1.0), SNaN) = min(0.0, SNaN) = QNaN
338// Val = NaN,ieee = false or Val = QNaN,ieee = true
339// min(min(NaN, 0.0), 1.0) = min(0.0, 1.0) = 0.0
340// min(min(NaN, 1.0), 0.0) = min(1.0, 0.0) = 0.0
341// min(min(0.0, 1.0), NaN) = min(0.0, NaN) = 0.0
342bool AMDGPURegBankCombinerImpl::matchFPMed3ToClamp(MachineInstr &MI,
343 Register &Reg) const {
344 // Perform combine only when the destination is a VGPR.
345 if (!isVgprRegBank(MI.getOperand(0).getReg()))
346 return false;
347
348 // In llvm-ir, clamp is often represented as an intrinsic call to
349 // @llvm.amdgcn.fmed3.f32(%Val, 0.0, 1.0). Check for other operand orders.
350 MachineInstr *Src0 = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
351 MachineInstr *Src1 = getDefIgnoringCopies(MI.getOperand(2).getReg(), MRI);
352 MachineInstr *Src2 = getDefIgnoringCopies(MI.getOperand(3).getReg(), MRI);
353
354 if (isFCst(Src0) && !isFCst(Src1))
355 std::swap(Src0, Src1);
356 if (isFCst(Src1) && !isFCst(Src2))
357 std::swap(Src1, Src2);
358 if (isFCst(Src0) && !isFCst(Src1))
359 std::swap(Src0, Src1);
360 if (!isClampZeroToOne(Src1, Src2))
361 return false;
362
363 Register Val = Src0->getOperand(0).getReg();
364
365 auto isOp3Zero = [&]() {
366 MachineInstr *Op3 = getDefIgnoringCopies(MI.getOperand(3).getReg(), MRI);
367 if (Op3->getOpcode() == TargetOpcode::G_FCONSTANT)
368 return Op3->getOperand(1).getFPImm()->isPosZero();
369 return false;
370 };
371 // For IEEE=false perform combine only when it's safe to assume that there are
372 // no NaN inputs. Most often MI is marked with nnan fast math flag.
373 // For IEEE=true consider NaN inputs. Requires dx10_clamp = true. Safe to fold
374 // when Val could be QNaN. If Val can also be SNaN third input should be 0.0.
375 if (VT->isKnownNeverNaN(MI.getOperand(0).getReg()) ||
376 (getIEEE() && getDX10Clamp() &&
377 (VT->isKnownNeverSNaN(Val) || isOp3Zero()))) {
378 Reg = Val;
379 return true;
380 }
381
382 return false;
383}
384
385void AMDGPURegBankCombinerImpl::applyClamp(MachineInstr &MI,
386 Register &Reg) const {
387 B.buildInstr(AMDGPU::G_AMDGPU_CLAMP, {MI.getOperand(0)}, {Reg},
388 MI.getFlags());
389 MI.eraseFromParent();
390}
391
392void AMDGPURegBankCombinerImpl::applyMed3(MachineInstr &MI,
393 Med3MatchInfo &MatchInfo) const {
394 B.buildInstr(MatchInfo.Opc, {MI.getOperand(0)},
395 {getAsVgpr(MatchInfo.Val0), getAsVgpr(MatchInfo.Val1),
396 getAsVgpr(MatchInfo.Val2)},
397 MI.getFlags());
398 MI.eraseFromParent();
399}
400
401void AMDGPURegBankCombinerImpl::applyCanonicalizeZextShiftAmt(
402 MachineInstr &MI, MachineInstr &Ext) const {
403 unsigned ShOpc = MI.getOpcode();
404 assert(ShOpc == AMDGPU::G_SHL || ShOpc == AMDGPU::G_LSHR ||
405 ShOpc == AMDGPU::G_ASHR);
406 assert(Ext.getOpcode() == AMDGPU::G_ZEXT);
407
408 Register AmtReg = Ext.getOperand(1).getReg();
409 Register ShDst = MI.getOperand(0).getReg();
410 Register ShSrc = MI.getOperand(1).getReg();
411
412 LLT ExtAmtTy = MRI.getType(Ext.getOperand(0).getReg());
413 LLT AmtTy = MRI.getType(AmtReg);
414
415 auto &RB = *MRI.getRegBank(AmtReg);
416
417 auto NewExt = B.buildAnyExt(ExtAmtTy, AmtReg);
418 auto Mask = B.buildConstant(
420 auto And = B.buildAnd(ExtAmtTy, NewExt, Mask);
421 B.buildInstr(ShOpc, {ShDst}, {ShSrc, And});
422
423 MRI.setRegBank(NewExt.getReg(0), RB);
424 MRI.setRegBank(Mask.getReg(0), RB);
425 MRI.setRegBank(And.getReg(0), RB);
426 MI.eraseFromParent();
427}
428
429bool AMDGPURegBankCombinerImpl::combineD16Load(MachineInstr &MI) const {
430 Register Dst;
431 MachineInstr *Load;
432 GSExtLoad *SextLoad;
433 GZExtLoad *ZextLoad;
434 const MachineMemOperand *MMO;
435 const int64_t CleanLo16 = 0xFFFFFFFFFFFF0000;
436 const int64_t CleanHi16 = 0x000000000000FFFF;
437
438 auto TryApplyD16Load = [&](unsigned D16Opc, GAnyLoad *SmallLoad) {
439 LLT DstTy = MRI.getType(Dst);
440 if (DstTy != LLT::fixed_vector(2, LLT::integer(16)) &&
441 DstTy != LLT::fixed_vector(2, LLT::float16()))
442 return false;
443
444 MachineInstr *DstDef = MRI.getVRegDef(Dst);
445 MachineInstr *InsertPt = SmallLoad;
446 if (SmallLoad->isSimple() && isObviouslySafeToFold(*SmallLoad, MI))
447 InsertPt = &MI;
448 else if (!Helper.dominates(*DstDef, *SmallLoad))
449 return false;
450
451 applyD16Load(D16Opc, MI, SmallLoad, Dst, *InsertPt);
452 return true;
453 };
454
455 // Load lo
456 if (mi_match(MI.getOperand(1).getReg(), MRI,
458 m_Copy(m_SpecificICst(CleanLo16))),
459 m_OneUse(m_MInstr(Load)))))) {
460 if (mi_match(Load, MRI, m_GZExtLoad(ZextLoad, m_MMO(MMO)))) {
461 unsigned LoadSize = MMO->getSizeInBits().getValue();
462 if (LoadSize == 8)
463 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO_U8, ZextLoad);
464 if (LoadSize == 16)
465 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO, ZextLoad);
466
467 return false;
468 }
469
470 // s32 Load_lo16 holds SextLoad i8, Load_hi16 is zero.
471 // fake16: and (sextload i8 -> s32), 0xFFFF
472 // true16: zext (sextload i8 -> s16) -> s32
473 if (mi_match(Load, MRI,
474 m_any_of(m_GAnd(m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO))),
475 m_Copy(m_SpecificICst(CleanHi16))),
478 m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO)))))))) {
479 unsigned LoadSize = MMO->getSizeInBits().getValue();
480 if (LoadSize == 8)
481 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO_I8, SextLoad);
482 }
483
484 return false;
485 }
486
487 // Load hi
488 if (mi_match(MI.getOperand(1).getReg(), MRI,
490 m_Copy(m_SpecificICst(CleanHi16))),
492 m_Copy(m_SpecificICst(16)))))))) {
493 if (mi_match(Load, MRI, m_GZExtLoad(ZextLoad, m_MMO(MMO)))) {
494 unsigned LoadSize = MMO->getSizeInBits().getValue();
495 if (LoadSize == 8)
496 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI_U8, ZextLoad);
497 if (LoadSize == 16)
498 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI, ZextLoad);
499
500 return false;
501 }
502
503 // The shift discards the upper half of the sign-extending load.
504 if (mi_match(Load, MRI,
505 m_any_of(m_GSExtLoad(SextLoad, m_MMO(MMO)),
506 m_GAnd(m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO))),
507 m_Copy(m_SpecificICst(CleanHi16))),
510 m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO)))))))) {
511 unsigned LoadSize = MMO->getSizeInBits().getValue();
512 if (LoadSize == 8)
513 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI_I8, SextLoad);
514 }
515
516 return false;
517 }
518
519 return false;
520}
521
522void AMDGPURegBankCombinerImpl::applyMinMaxToMinMax3(
523 MachineInstr &MI, MinMaxToMinMax3MatchInfo &MatchInfo) const {
524 B.buildInstr(MatchInfo.Opc, {MI.getOperand(0)},
525 {MatchInfo.Val0, MatchInfo.Val1, MatchInfo.Val2}, MI.getFlags());
526 MI.eraseFromParent();
527 return;
528}
529
530bool AMDGPURegBankCombinerImpl::matchCombine_mul24(MachineInstr &MI) const {
531 Register Dst = MI.getOperand(0).getReg();
532 if (MRI.getType(Dst) != LLT::scalar(32) || !isVgprRegBank(Dst))
533 return false;
534 Register Src0 = MI.getOperand(1).getReg();
535 Register Src1 = MI.getOperand(2).getReg();
536 if (STI.hasMulU24() && VT->getKnownBits(Src0).countMinLeadingZeros() >= 8 &&
537 VT->getKnownBits(Src1).countMinLeadingZeros() >= 8)
538 return true;
539 return STI.hasMulI24() && VT->computeNumSignBits(Src0) >= 9 &&
540 VT->computeNumSignBits(Src1) >= 9;
541}
542
543void AMDGPURegBankCombinerImpl::applyCombine_mul24(MachineInstr &MI) const {
544 Register Dst = MI.getOperand(0).getReg();
545 Register Src0 = MI.getOperand(1).getReg();
546 Register Src1 = MI.getOperand(2).getReg();
547 Intrinsic::ID IntrID =
548 (STI.hasMulU24() && VT->getKnownBits(Src0).countMinLeadingZeros() >= 8 &&
549 VT->getKnownBits(Src1).countMinLeadingZeros() >= 8)
550 ? Intrinsic::amdgcn_mul_u24
551 : Intrinsic::amdgcn_mul_i24;
552 B.buildIntrinsic(IntrID, ArrayRef<Register>{Dst}).addUse(Src0).addUse(Src1);
553 MI.eraseFromParent();
554}
555
556// min(min(a, b), c) == min(a, min(b, c)) == min3(a, b, c)
557// supported scalar type: S32 S16 U32 U16 F32 F16
558bool AMDGPURegBankCombinerImpl::matchMinMaxToMinMax3(
559 MachineInstr &MI, MinMaxToMinMax3MatchInfo &MatchInfo) const {
560 Register Dst = MI.getOperand(0).getReg();
561 Register Src1 = MI.getOperand(1).getReg();
562 Register Src2 = MI.getOperand(2).getReg();
563 // If the register is SGPR, don't optimize it.
564 if (!(isVgprRegBank(Dst) && isVgprRegBank(Src1) && isVgprRegBank(Src2))) {
565 return false;
566 }
567
568 LLT Ty = MRI.getType(Dst);
569 unsigned Opc = MI.getOpcode();
570 if (!(Ty == LLT::scalar(32) ||
571 (Ty == LLT::scalar(16) && STI.hasMin3Max3_16())))
572 return false;
573
574 Register R0, R1, R2;
575 if (!mi_match(MI, MRI,
577 m_OneUse(m_BinOp(Opc, m_Reg(R0), m_Reg(R1))),
578 m_Reg(R2)))) {
579 return false;
580 }
581
582 unsigned AMDGPUOpc = 0;
583 switch (Opc) {
584 case AMDGPU::G_SMAX:
585 AMDGPUOpc = AMDGPU::G_AMDGPU_SMAX3;
586 break;
587 case AMDGPU::G_SMIN:
588 AMDGPUOpc = AMDGPU::G_AMDGPU_SMIN3;
589 break;
590 case AMDGPU::G_UMAX:
591 AMDGPUOpc = AMDGPU::G_AMDGPU_UMAX3;
592 break;
593 case AMDGPU::G_UMIN:
594 AMDGPUOpc = AMDGPU::G_AMDGPU_UMIN3;
595 break;
596 case AMDGPU::G_FMAXNUM:
597 case AMDGPU::G_FMAXNUM_IEEE:
598 case AMDGPU::G_FMAXIMUMNUM:
599 AMDGPUOpc = AMDGPU::G_AMDGPU_FMAX3;
600 break;
601 case AMDGPU::G_FMINNUM:
602 case AMDGPU::G_FMINNUM_IEEE:
603 case AMDGPU::G_FMINIMUMNUM:
604 AMDGPUOpc = AMDGPU::G_AMDGPU_FMIN3;
605 break;
606 case AMDGPU::G_FMAXIMUM:
607 AMDGPUOpc = AMDGPU::G_AMDGPU_FMAXIMUM3;
608 break;
609 case AMDGPU::G_FMINIMUM:
610 AMDGPUOpc = AMDGPU::G_AMDGPU_FMINIMUM3;
611 break;
612 default:
613 return false;
614 }
615
616 MatchInfo = {AMDGPUOpc, R0, R1, R2};
617 return true;
618}
619
620void AMDGPURegBankCombinerImpl::applyD16Load(unsigned D16Opc,
621 MachineInstr &DstMI,
622 MachineInstr *SmallLoad,
623 Register SrcReg32ToOverwriteD16,
624 MachineInstr &InsertPt) const {
625 Register DstReg = DstMI.getOperand(0).getReg();
626 LLT SrcTy = MRI.getType(SrcReg32ToOverwriteD16);
627
628 // Dst and Src for D16 load need to have same type.
629 Register D16Dst =
630 SrcTy == MRI.getType(DstReg)
631 ? DstReg
632 : MRI.createVirtualRegister({MRI.getRegBank(DstReg), SrcTy});
633
634 B.setInstrAndDebugLoc(InsertPt);
635 B.setDebugLoc(SmallLoad->getDebugLoc());
636 B.buildInstr(D16Opc, {D16Dst},
637 {SmallLoad->getOperand(1).getReg(), SrcReg32ToOverwriteD16})
638 .setMemRefs(SmallLoad->memoperands());
639
640 if (D16Dst != DstReg)
641 B.buildBitcast(DstReg, D16Dst);
642
643 // Recursive cleanup may erase SmallLoad.
644 Register LoadDst = SmallLoad->getOperand(0).getReg();
645 eraseInstr(DstMI, MRI);
646 // Volatile and ordered atomic loads are not trivially dead.
647 if (MachineInstr *OldLoad = MRI.getVRegDef(LoadDst))
648 eraseInstr(*OldLoad, MRI);
649}
650
651SIModeRegisterDefaults AMDGPURegBankCombinerImpl::getMode() const {
652 return MF.getInfo<SIMachineFunctionInfo>()->getMode();
653}
654
655bool AMDGPURegBankCombinerImpl::getIEEE() const { return getMode().IEEE; }
656
657bool AMDGPURegBankCombinerImpl::getDX10Clamp() const {
658 return getMode().DX10Clamp;
659}
660
661bool AMDGPURegBankCombinerImpl::isFminnumIeee(const MachineInstr &MI) const {
662 return MI.getOpcode() == AMDGPU::G_FMINNUM_IEEE;
663}
664
665bool AMDGPURegBankCombinerImpl::isFminnum(const MachineInstr &MI) const {
666 return MI.getOpcode() == AMDGPU::G_FMINNUM;
667}
668
669bool AMDGPURegBankCombinerImpl::isFminnumLike(const MachineInstr &MI) const {
670 return isFminnumIeee(MI) || isFminnum(MI);
671}
672
673bool AMDGPURegBankCombinerImpl::isFCst(MachineInstr *MI) const {
674 return MI->getOpcode() == AMDGPU::G_FCONSTANT;
675}
676
677bool AMDGPURegBankCombinerImpl::isClampZeroToOne(MachineInstr *K0,
678 MachineInstr *K1) const {
679 if (isFCst(K0) && isFCst(K1)) {
680 const ConstantFP *KO_FPImm = K0->getOperand(1).getFPImm();
681 const ConstantFP *K1_FPImm = K1->getOperand(1).getFPImm();
682 return (KO_FPImm->isPosZero() && K1_FPImm->isOne()) ||
683 (KO_FPImm->isOne() && K1_FPImm->isPosZero());
684 }
685 return false;
686}
687
688static bool runCombiner(MachineFunction &MF,
689 function_ref<GISelValueTracking *()> GetVT,
690 function_ref<MachineDominatorTree *()> GetMDT,
691 bool EnableOpt) {
692 AMDGPURegBankCombinerImplRuleConfig RuleConfig;
693 if (!RuleConfig.parseCommandLineOption())
694 reportFatalUsageError("Invalid rule identifier");
695
696 if (MF.getProperties().hasFailedISel())
697 return false;
698
699 const Function &F = MF.getFunction();
700 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
701 const auto *LI = ST.getLegalizerInfo();
702
703 CombinerInfo CInfo(/*AllowIllegalOps=*/false, /*ShouldLegalizeIllegal=*/true,
704 LI, EnableOpt, F.hasOptSize(), F.hasMinSize());
705 // Disable fixed-point iteration to reduce compile-time
706 CInfo.MaxIterations = 1;
707 CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
708 // RegBankSelect seems not to leave dead instructions, so a full DCE pass is
709 // unnecessary.
710 CInfo.EnableFullDCE = false;
711
712 GISelValueTracking *VT = GetVT();
713 MachineDominatorTree *MDT = GetMDT();
714 AMDGPURegBankCombinerImpl Impl(MF, CInfo, *VT, /*CSEInfo=*/nullptr,
715 RuleConfig, ST, MDT, LI);
716 return Impl.combineMachineInstrs();
717}
718
719// Pass boilerplate
720// ================
721
722class AMDGPURegBankCombinerLegacy : public MachineFunctionPass {
723public:
724 static char ID;
725
726 AMDGPURegBankCombinerLegacy(bool IsOptLevelNone = false)
727 : MachineFunctionPass(ID), IsOptLevelNone(IsOptLevelNone) {}
728
729 StringRef getPassName() const override { return "AMDGPURegBankCombiner"; }
730
731 bool runOnMachineFunction(MachineFunction &MF) override;
732
733 void getAnalysisUsage(AnalysisUsage &AU) const override;
734
735private:
736 bool IsOptLevelNone;
737};
738} // end anonymous namespace
739
740void AMDGPURegBankCombinerLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
741 AU.setPreservesCFG();
743 AU.addRequired<GISelValueTrackingAnalysisLegacy>();
744 AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
745 if (!IsOptLevelNone) {
746 AU.addRequired<MachineDominatorTreeWrapperPass>();
747 }
749}
750
751bool AMDGPURegBankCombinerLegacy::runOnMachineFunction(MachineFunction &MF) {
752 const Function &F = MF.getFunction();
753 bool EnableOpt =
754 MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
755
756 return runCombiner(
757 MF,
758 [&]() {
759 return &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
760 },
761 [&]() -> MachineDominatorTree * {
762 return IsOptLevelNone ? nullptr
763 : &getAnalysis<MachineDominatorTreeWrapperPass>()
764 .getDomTree();
765 },
766 EnableOpt);
767}
768
769char AMDGPURegBankCombinerLegacy::ID = 0;
770INITIALIZE_PASS_BEGIN(AMDGPURegBankCombinerLegacy, DEBUG_TYPE,
771 "Combine AMDGPU machine instrs after regbankselect",
772 false, false)
774INITIALIZE_PASS_END(AMDGPURegBankCombinerLegacy, DEBUG_TYPE,
775 "Combine AMDGPU machine instrs after regbankselect", false,
776 false)
777
779 return new AMDGPURegBankCombinerLegacy(IsOptLevelNone);
780}
781
783 : IsOptLevelNone(IsOptLevelNone) {}
784
788 const Function &F = MF.getFunction();
789 bool EnableOpt = MF.getTarget().getOptLevel() != CodeGenOptLevel::None &&
790 !F.hasOptNone() && !shouldSkipOptimizationForOptBisect(F);
791
792 if (!runCombiner(
793 MF, [&]() { return &MFAM.getResult<GISelValueTrackingAnalysis>(MF); },
794 [&]() -> MachineDominatorTree * {
795 return IsOptLevelNone
796 ? nullptr
798 },
799 EnableOpt))
800 return PreservedAnalyses::all();
801
805 return PA;
806}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define GET_GICOMBINER_CONSTRUCTOR_INITS
This file declares the targeting of the Machinelegalizer class for AMDGPU.
This file declares the targeting of the RegisterBankInfo class for AMDGPU.
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
This contains common combine transformations that may be used in a combine pass,or by the target else...
Option class for Targets to specify which operations are combined how and when.
This contains the base class for all Combiners generated by TableGen.
AMD GCN specific subclass of TargetSubtarget.
Provides analysis for querying information about KnownBits during GISel passes.
#define DEBUG_TYPE
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Contains matchers for matching SSA Machine Instructions.
Register Reg
Register const TargetRegisterInfo * TRI
#define R2(n)
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
static StringRef getName(Value *V)
MachineRegisterInfo & MRI
static bool isClampZeroToOne(SDValue A, SDValue B)
Target-Independent Code Generator Pass Configuration Options pass.
AMDGPURegBankCombinerPass(bool IsOptLevelNone=false)
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:280
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
LLVM_ABI bool dominates(const MachineInstr &DefMI, const MachineInstr &UseMI) const
Returns true if DefMI dominates UseMI.
Combiner implementation.
Definition Combiner.h:33
bool isPosZero() const
Return true if the value is positive zero.
Definition Constants.h:470
bool isOne() const
Returns true if this value is exactly +1.0.
Definition Constants.h:485
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:312
bool hasMin3Max3_16() const
bool hasMed3_16() const
The CSE Analysis object.
Definition CSEInfo.h:72
To use KnownBitsInfo analysis in a pass, KnownBitsInfo &Info = getAnalysis<GISelValueTrackingInfoAnal...
constexpr unsigned getScalarSizeInBits() const
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
static constexpr LLT float16()
Get a 16-bit IEEE half value.
static constexpr LLT fixed_vector(unsigned NumElements, unsigned ScalarSizeInBits)
Get a low-level fixed-width vector of some number of elements and element width.
static LLT integer(unsigned SizeInBits)
TypeSize getValue() const
Analysis pass which computes a MachineDominatorTree.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineFunctionProperties & getProperties() const
Get the function properties.
const TargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
ArrayRef< MachineMemOperand * > memoperands() const
Access to memory operands of the instruction.
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
LocationSize getSizeInBits() const
Return the size in bits of the memory reference.
Register getReg() const
getReg - Returns the register number.
const ConstantFP * getFPImm() const
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI bool hasOneNonDBGUse(Register RegNo) const
hasOneNonDBGUse - Return true if there is exactly one non-Debug use of the specified register.
LLVM_ABI LLVM_READONLY MachineInstr * getVRegDef(Register Reg) const
getVRegDef - Return the machine instr that defines the specified virtual register or null if none is ...
const RegisterBank * getRegBank(Register Reg) const
Return the register bank of Reg.
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
LLT getType(Register Reg) const
Get the low-level type of Reg or LLT{} if Reg is not a generic (target independent) virtual register.
LLVM_ABI void setRegBank(Register Reg, const RegisterBank &RegBank)
Set the register bank to RegBank for Reg.
iterator_range< use_instr_iterator > use_instructions(Register Reg) const
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
PreservedAnalyses & preserve()
Mark an analysis as preserved.
Definition Analysis.h:132
Holds all the information related to register banks.
const RegisterBank & getRegBank(unsigned ID)
Get the register bank identified by ID.
unsigned getID() const
Get the identifier of this register bank.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
CodeGenOptLevel getOptLevel() const
Returns the optimization level: None, Less, Default, or Aggressive.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
operand_type_match m_Reg()
SpecificConstantMatch m_SpecificICst(const APInt &RequestedValue)
Matches a constant equal to RequestedValue.
UnaryOp_match< SrcTy, TargetOpcode::COPY > m_Copy(SrcTy &&Src)
UnaryOp_match< SrcTy, TargetOpcode::G_ZEXT > m_GZExt(const SrcTy &Src)
LoadOp_match< GZExtLoad, operand_type_match > m_GZExtLoad(GZExtLoad *&Inst)
LoadOp_match< GSExtLoad, operand_type_match > m_GSExtLoad(GSExtLoad *&Inst)
BinaryOp_match< LHS, RHS, TargetOpcode::G_OR, true > m_GOr(const LHS &L, const RHS &R)
CheckType m_SpecificType(LLT Ty)
BinaryOpc_match< LHS, RHS, true > m_CommutativeBinOp(unsigned Opcode, const LHS &L, const RHS &R)
MMORef m_MMO(const MachineMemOperand *&MMO)
bool mi_match(Reg R, const MachineRegisterInfo &MRI, Pattern &&P)
BinaryOp_match< LHS, RHS, TargetOpcode::G_SHL, false > m_GShl(const LHS &L, const RHS &R)
Or< Preds... > m_any_of(Preds &&... preds)
BinaryOp_match< LHS, RHS, TargetOpcode::G_AND, true > m_GAnd(const LHS &L, const RHS &R)
UnaryOp_match< SrcTy, TargetOpcode::G_BITCAST > m_GBitcast(const SrcTy &Src)
bind_ty< MachineInstr * > m_MInstr(MachineInstr *&MI)
And< Preds... > m_all_of(Preds &&... preds)
OneUse_match< SubPat > m_OneUse(const SubPat &SP)
auto m_BinOp()
Match an arbitrary binary operation and ignore it.
NodeAddr< DefNode * > Def
Definition RDFGraph.h:384
NodeAddr< UseNode * > Use
Definition RDFGraph.h:385
This is an optimization pass for GlobalISel generic memory operations.
@ Load
The value being inserted comes from a load (InsertElement only).
FunctionPass * createAMDGPURegBankCombinerLegacy(bool IsOptLevelNone)
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI MachineInstr * getDefIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI)
Find the def instruction for Reg, folding away any trivial copies.
Definition Utils.cpp:520
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
LLVM_ABI void getSelectionDAGFallbackAnalysisUsage(AnalysisUsage &AU)
Modify analysis usage so it preserves passes required for the SelectionDAG fallback.
Definition Utils.cpp:1296
@ And
Bitwise or logical AND of integers.
ArrayRef(const T &OneElt) -> ArrayRef< T >
LLVM_ABI void eraseInstr(MachineInstr &MI, MachineRegisterInfo &MRI, LostDebugLocObserver *LocObserver=nullptr)
Definition Utils.cpp:1832
constexpr T maskTrailingOnes(unsigned N)
Create a bitmask with the N right-most bits set to 1, and all other bits set to 0.
Definition MathExtras.h:78
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:881