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"
30
31#define GET_GICOMBINER_DEPS
32#include "AMDGPUGenPreLegalizeGICombiner.inc"
33#undef GET_GICOMBINER_DEPS
34
35#define DEBUG_TYPE "amdgpu-regbank-combiner"
36
37using namespace llvm;
38using namespace MIPatternMatch;
39
40namespace {
41#define GET_GICOMBINER_TYPES
42#include "AMDGPUGenRegBankGICombiner.inc"
43#undef GET_GICOMBINER_TYPES
44
45class AMDGPURegBankCombinerImpl : public Combiner {
46protected:
47 const AMDGPURegBankCombinerImplRuleConfig &RuleConfig;
48 const GCNSubtarget &STI;
49 const RegisterBankInfo &RBI;
51 const SIInstrInfo &TII;
52 const CombinerHelper Helper;
53
54public:
55 AMDGPURegBankCombinerImpl(
57 GISelCSEInfo *CSEInfo,
58 const AMDGPURegBankCombinerImplRuleConfig &RuleConfig,
59 const GCNSubtarget &STI, MachineDominatorTree *MDT,
60 const LegalizerInfo *LI);
61
62 static const char *getName() { return "AMDGPURegBankCombinerImpl"; }
63
64 bool tryCombineAll(MachineInstr &I) const override;
65
66 bool isVgprRegBank(Register Reg) const;
67 Register getAsVgpr(Register Reg) const;
68
69 struct MinMaxMedOpc {
70 unsigned Min, Max, Med;
71 };
72
73 struct Med3MatchInfo {
74 unsigned Opc;
75 Register Val0, Val1, Val2;
76 };
77
78 struct MinMaxToMinMax3MatchInfo {
79 unsigned Opc;
80 Register Val0, Val1, Val2;
81 };
82
83 MinMaxMedOpc getMinMaxPair(unsigned Opc) const;
84
85 template <class m_Cst, typename CstTy>
86 bool matchMed(MachineInstr &MI, MachineRegisterInfo &MRI, MinMaxMedOpc MMMOpc,
87 Register &Val, CstTy &K0, CstTy &K1) const;
88
89 bool matchIntMinMaxToMed3(MachineInstr &MI, Med3MatchInfo &MatchInfo) const;
90 bool matchFPMinMaxToMed3(MachineInstr &MI, Med3MatchInfo &MatchInfo) const;
91 bool matchFPMinMaxToClamp(MachineInstr &MI, Register &Reg) const;
92 bool matchFPMed3ToClamp(MachineInstr &MI, Register &Reg) const;
93 void applyMed3(MachineInstr &MI, Med3MatchInfo &MatchInfo) const;
94 void applyClamp(MachineInstr &MI, Register &Reg) const;
95
96 void applyCanonicalizeZextShiftAmt(MachineInstr &MI, MachineInstr &Ext) const;
97
98 bool combineD16Load(MachineInstr &MI) const;
99 void applyD16Load(unsigned D16Opc, MachineInstr &DstMI,
100 MachineInstr *SmallLoad, Register ToOverwriteD16,
101 MachineInstr &InsertPt) const;
102
103 bool matchMinMaxToMinMax3(MachineInstr &MI,
104 MinMaxToMinMax3MatchInfo &MatchInfo) const;
105 void applyMinMaxToMinMax3(MachineInstr &MI,
106 MinMaxToMinMax3MatchInfo &MatchInfo) const;
107
108private:
109 SIModeRegisterDefaults getMode() const;
110 bool getIEEE() const;
111 bool getDX10Clamp() const;
112 bool isFminnumIeee(const MachineInstr &MI) const;
113 bool isFminnum(const MachineInstr &MI) const;
114 bool isFminnumLike(const MachineInstr &MI) const;
115 bool isFCst(MachineInstr *MI) const;
116 bool isClampZeroToOne(MachineInstr *K0, MachineInstr *K1) const;
117
118#define GET_GICOMBINER_CLASS_MEMBERS
119#define AMDGPUSubtarget GCNSubtarget
120#include "AMDGPUGenRegBankGICombiner.inc"
121#undef GET_GICOMBINER_CLASS_MEMBERS
122#undef AMDGPUSubtarget
123};
124
125#define GET_GICOMBINER_IMPL
126#define AMDGPUSubtarget GCNSubtarget
127#include "AMDGPUGenRegBankGICombiner.inc"
128#undef AMDGPUSubtarget
129#undef GET_GICOMBINER_IMPL
130
131AMDGPURegBankCombinerImpl::AMDGPURegBankCombinerImpl(
133 GISelCSEInfo *CSEInfo,
134 const AMDGPURegBankCombinerImplRuleConfig &RuleConfig,
135 const GCNSubtarget &STI, MachineDominatorTree *MDT, const LegalizerInfo *LI)
136 : Combiner(MF, CInfo, &VT, CSEInfo), RuleConfig(RuleConfig), STI(STI),
137 RBI(*STI.getRegBankInfo()), TRI(*STI.getRegisterInfo()),
138 TII(*STI.getInstrInfo()),
139 Helper(Observer, B, /*IsPreLegalize*/ false, &VT, MDT, LI),
141#include "AMDGPUGenRegBankGICombiner.inc"
143{
144}
145
146bool AMDGPURegBankCombinerImpl::isVgprRegBank(Register Reg) const {
147 return RBI.getRegBank(Reg, MRI, TRI)->getID() == AMDGPU::VGPRRegBankID;
148}
149
150Register AMDGPURegBankCombinerImpl::getAsVgpr(Register Reg) const {
151 if (isVgprRegBank(Reg))
152 return Reg;
153
154 // Search for existing copy of Reg to vgpr.
155 for (MachineInstr &Use : MRI.use_instructions(Reg)) {
156 Register Def = Use.getOperand(0).getReg();
157 if (Use.getOpcode() == AMDGPU::COPY && isVgprRegBank(Def))
158 return Def;
159 }
160
161 // Copy Reg to vgpr.
162 Register VgprReg = B.buildCopy(MRI.getType(Reg), Reg).getReg(0);
163 MRI.setRegBank(VgprReg, RBI.getRegBank(AMDGPU::VGPRRegBankID));
164 return VgprReg;
165}
166
167AMDGPURegBankCombinerImpl::MinMaxMedOpc
168AMDGPURegBankCombinerImpl::getMinMaxPair(unsigned Opc) const {
169 switch (Opc) {
170 default:
171 llvm_unreachable("Unsupported opcode");
172 case AMDGPU::G_SMAX:
173 case AMDGPU::G_SMIN:
174 return {AMDGPU::G_SMIN, AMDGPU::G_SMAX, AMDGPU::G_AMDGPU_SMED3};
175 case AMDGPU::G_UMAX:
176 case AMDGPU::G_UMIN:
177 return {AMDGPU::G_UMIN, AMDGPU::G_UMAX, AMDGPU::G_AMDGPU_UMED3};
178 case AMDGPU::G_FMAXNUM:
179 case AMDGPU::G_FMINNUM:
180 return {AMDGPU::G_FMINNUM, AMDGPU::G_FMAXNUM, AMDGPU::G_AMDGPU_FMED3};
181 case AMDGPU::G_FMAXNUM_IEEE:
182 case AMDGPU::G_FMINNUM_IEEE:
183 return {AMDGPU::G_FMINNUM_IEEE, AMDGPU::G_FMAXNUM_IEEE,
184 AMDGPU::G_AMDGPU_FMED3};
185 }
186}
187
188template <class m_Cst, typename CstTy>
189bool AMDGPURegBankCombinerImpl::matchMed(MachineInstr &MI,
190 MachineRegisterInfo &MRI,
191 MinMaxMedOpc MMMOpc, Register &Val,
192 CstTy &K0, CstTy &K1) const {
193 // 4 operand commutes of: min(max(Val, K0), K1).
194 // Find K1 from outer instr: min(max(...), K1) or min(K1, max(...)).
195 // Find K0 and Val from inner instr: max(K0, Val) or max(Val, K0).
196 // 4 operand commutes of: max(min(Val, K1), K0).
197 // Find K0 from outer instr: max(min(...), K0) or max(K0, min(...)).
198 // Find K1 and Val from inner instr: min(K1, Val) or min(Val, K1).
199 return mi_match(
200 MI, MRI,
201 m_any_of(
203 MMMOpc.Min, m_CommutativeBinOp(MMMOpc.Max, m_Reg(Val), m_Cst(K0)),
204 m_Cst(K1)),
206 MMMOpc.Max, m_CommutativeBinOp(MMMOpc.Min, m_Reg(Val), m_Cst(K1)),
207 m_Cst(K0))));
208}
209
210bool AMDGPURegBankCombinerImpl::matchIntMinMaxToMed3(
211 MachineInstr &MI, Med3MatchInfo &MatchInfo) const {
212 Register Dst = MI.getOperand(0).getReg();
213 if (!isVgprRegBank(Dst))
214 return false;
215
216 // med3 for i16 is only available on gfx9+, and not available for v2i16.
217 LLT Ty = MRI.getType(Dst);
218 if ((Ty != LLT::scalar(16) || !STI.hasMed3_16()) && Ty != LLT::scalar(32))
219 return false;
220
221 MinMaxMedOpc OpcodeTriple = getMinMaxPair(MI.getOpcode());
222 Register Val;
223 std::optional<ValueAndVReg> K0, K1;
224 // Match min(max(Val, K0), K1) or max(min(Val, K1), K0). Then see if K0 <= K1.
225 if (!matchMed<GCstAndRegMatch>(MI, MRI, OpcodeTriple, Val, K0, K1))
226 return false;
227
228 if (OpcodeTriple.Med == AMDGPU::G_AMDGPU_SMED3 && K0->Value.sgt(K1->Value))
229 return false;
230 if (OpcodeTriple.Med == AMDGPU::G_AMDGPU_UMED3 && K0->Value.ugt(K1->Value))
231 return false;
232
233 MatchInfo = {OpcodeTriple.Med, Val, K0->VReg, K1->VReg};
234 return true;
235}
236
237// fmed3(NaN, K0, K1) = min(min(NaN, K0), K1)
238// ieee = true : min/max(SNaN, K) = QNaN, min/max(QNaN, K) = K
239// ieee = false : min/max(NaN, K) = K
240// clamp(NaN) = dx10_clamp ? 0.0 : NaN
241// Consider values of min(max(Val, K0), K1) and max(min(Val, K1), K0) as input.
242// Other operand commutes (see matchMed) give same result since min and max are
243// commutative.
244
245// Try to replace fp min(max(Val, K0), K1) or max(min(Val, K1), K0), KO<=K1
246// with fmed3(Val, K0, K1) or clamp(Val). Clamp requires K0 = 0.0 and K1 = 1.0.
247// Val = SNaN only for ieee = true
248// fmed3(SNaN, K0, K1) = min(min(SNaN, K0), K1) = min(QNaN, K1) = K1
249// min(max(SNaN, K0), K1) = min(QNaN, K1) = K1
250// max(min(SNaN, K1), K0) = max(K1, K0) = K1
251// Val = NaN,ieee = false or Val = QNaN,ieee = true
252// fmed3(NaN, K0, K1) = min(min(NaN, K0), K1) = min(K0, K1) = K0
253// min(max(NaN, K0), K1) = min(K0, K1) = K0 (can clamp when dx10_clamp = true)
254// max(min(NaN, K1), K0) = max(K1, K0) = K1 != K0
255bool AMDGPURegBankCombinerImpl::matchFPMinMaxToMed3(
256 MachineInstr &MI, Med3MatchInfo &MatchInfo) const {
257 Register Dst = MI.getOperand(0).getReg();
258 // Perform combine only when the destination is a VGPR.
259 if (!isVgprRegBank(Dst))
260 return false;
261
262 LLT Ty = MRI.getType(Dst);
263
264 // med3 for f16 is only available on gfx9+, and not available for v2f16.
265 if ((Ty != LLT::scalar(16) || !STI.hasMed3_16()) && Ty != LLT::scalar(32))
266 return false;
267
268 auto OpcodeTriple = getMinMaxPair(MI.getOpcode());
269
270 Register Val;
271 std::optional<FPValueAndVReg> K0, K1;
272 // Match min(max(Val, K0), K1) or max(min(Val, K1), K0). Then see if K0 <= K1.
273 if (!matchMed<GFCstAndRegMatch>(MI, MRI, OpcodeTriple, Val, K0, K1))
274 return false;
275
276 if (K0->Value > K1->Value)
277 return false;
278
279 // For IEEE=false perform combine only when it's safe to assume that there are
280 // no NaN inputs. Most often MI is marked with nnan fast math flag.
281 // For IEEE=true consider NaN inputs. fmed3(NaN, K0, K1) is equivalent to
282 // min(min(NaN, K0), K1). Safe to fold for min(max(Val, K0), K1) since inner
283 // nodes(max/min) have same behavior when one input is NaN and other isn't.
284 // Don't consider max(min(SNaN, K1), K0) since there is no isKnownNeverQNaN,
285 // also post-legalizer inputs to min/max are fcanonicalized (never SNaN).
286 if ((getIEEE() && isFminnumLike(MI)) || VT->isKnownNeverNaN(Dst)) {
287 // Don't fold single use constant that can't be inlined.
288 if ((!MRI.hasOneNonDBGUse(K0->VReg) || TII.isInlineConstant(K0->Value)) &&
289 (!MRI.hasOneNonDBGUse(K1->VReg) || TII.isInlineConstant(K1->Value))) {
290 MatchInfo = {OpcodeTriple.Med, Val, K0->VReg, K1->VReg};
291 return true;
292 }
293 }
294
295 return false;
296}
297
298bool AMDGPURegBankCombinerImpl::matchFPMinMaxToClamp(MachineInstr &MI,
299 Register &Reg) const {
300 // Perform combine only when the destination is a VGPR.
301 if (!isVgprRegBank(MI.getOperand(0).getReg()))
302 return false;
303
304 // Clamp is available on all types after regbankselect (f16, f32, f64, v2f16).
305 auto OpcodeTriple = getMinMaxPair(MI.getOpcode());
306 Register Val;
307 std::optional<FPValueAndVReg> K0, K1;
308 // Match min(max(Val, K0), K1) or max(min(Val, K1), K0).
309 if (!matchMed<GFCstOrSplatGFCstMatch>(MI, MRI, OpcodeTriple, Val, K0, K1))
310 return false;
311
312 if (!K0->Value.isPosZero() || !K1->Value.isOne())
313 return false;
314
315 // For IEEE=false perform combine only when it's safe to assume that there are
316 // no NaN inputs. Most often MI is marked with nnan fast math flag.
317 // For IEEE=true consider NaN inputs. Only min(max(QNaN, 0.0), 1.0) evaluates
318 // to 0.0 requires dx10_clamp = true.
319 if ((getIEEE() && getDX10Clamp() && isFminnumLike(MI) &&
320 VT->isKnownNeverSNaN(Val)) ||
321 VT->isKnownNeverNaN(MI.getOperand(0).getReg())) {
322 Reg = Val;
323 return true;
324 }
325
326 return false;
327}
328
329// Replacing fmed3(NaN, 0.0, 1.0) with clamp. Requires dx10_clamp = true.
330// Val = SNaN only for ieee = true. It is important which operand is NaN.
331// min(min(SNaN, 0.0), 1.0) = min(QNaN, 1.0) = 1.0
332// min(min(SNaN, 1.0), 0.0) = min(QNaN, 0.0) = 0.0
333// min(min(0.0, 1.0), SNaN) = min(0.0, SNaN) = QNaN
334// Val = NaN,ieee = false or Val = QNaN,ieee = true
335// min(min(NaN, 0.0), 1.0) = min(0.0, 1.0) = 0.0
336// min(min(NaN, 1.0), 0.0) = min(1.0, 0.0) = 0.0
337// min(min(0.0, 1.0), NaN) = min(0.0, NaN) = 0.0
338bool AMDGPURegBankCombinerImpl::matchFPMed3ToClamp(MachineInstr &MI,
339 Register &Reg) const {
340 // Perform combine only when the destination is a VGPR.
341 if (!isVgprRegBank(MI.getOperand(0).getReg()))
342 return false;
343
344 // In llvm-ir, clamp is often represented as an intrinsic call to
345 // @llvm.amdgcn.fmed3.f32(%Val, 0.0, 1.0). Check for other operand orders.
346 MachineInstr *Src0 = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
347 MachineInstr *Src1 = getDefIgnoringCopies(MI.getOperand(2).getReg(), MRI);
348 MachineInstr *Src2 = getDefIgnoringCopies(MI.getOperand(3).getReg(), MRI);
349
350 if (isFCst(Src0) && !isFCst(Src1))
351 std::swap(Src0, Src1);
352 if (isFCst(Src1) && !isFCst(Src2))
353 std::swap(Src1, Src2);
354 if (isFCst(Src0) && !isFCst(Src1))
355 std::swap(Src0, Src1);
356 if (!isClampZeroToOne(Src1, Src2))
357 return false;
358
359 Register Val = Src0->getOperand(0).getReg();
360
361 auto isOp3Zero = [&]() {
362 MachineInstr *Op3 = getDefIgnoringCopies(MI.getOperand(3).getReg(), MRI);
363 if (Op3->getOpcode() == TargetOpcode::G_FCONSTANT)
364 return Op3->getOperand(1).getFPImm()->isPosZero();
365 return false;
366 };
367 // For IEEE=false perform combine only when it's safe to assume that there are
368 // no NaN inputs. Most often MI is marked with nnan fast math flag.
369 // For IEEE=true consider NaN inputs. Requires dx10_clamp = true. Safe to fold
370 // when Val could be QNaN. If Val can also be SNaN third input should be 0.0.
371 if (VT->isKnownNeverNaN(MI.getOperand(0).getReg()) ||
372 (getIEEE() && getDX10Clamp() &&
373 (VT->isKnownNeverSNaN(Val) || isOp3Zero()))) {
374 Reg = Val;
375 return true;
376 }
377
378 return false;
379}
380
381void AMDGPURegBankCombinerImpl::applyClamp(MachineInstr &MI,
382 Register &Reg) const {
383 B.buildInstr(AMDGPU::G_AMDGPU_CLAMP, {MI.getOperand(0)}, {Reg},
384 MI.getFlags());
385 MI.eraseFromParent();
386}
387
388void AMDGPURegBankCombinerImpl::applyMed3(MachineInstr &MI,
389 Med3MatchInfo &MatchInfo) const {
390 B.buildInstr(MatchInfo.Opc, {MI.getOperand(0)},
391 {getAsVgpr(MatchInfo.Val0), getAsVgpr(MatchInfo.Val1),
392 getAsVgpr(MatchInfo.Val2)},
393 MI.getFlags());
394 MI.eraseFromParent();
395}
396
397void AMDGPURegBankCombinerImpl::applyCanonicalizeZextShiftAmt(
398 MachineInstr &MI, MachineInstr &Ext) const {
399 unsigned ShOpc = MI.getOpcode();
400 assert(ShOpc == AMDGPU::G_SHL || ShOpc == AMDGPU::G_LSHR ||
401 ShOpc == AMDGPU::G_ASHR);
402 assert(Ext.getOpcode() == AMDGPU::G_ZEXT);
403
404 Register AmtReg = Ext.getOperand(1).getReg();
405 Register ShDst = MI.getOperand(0).getReg();
406 Register ShSrc = MI.getOperand(1).getReg();
407
408 LLT ExtAmtTy = MRI.getType(Ext.getOperand(0).getReg());
409 LLT AmtTy = MRI.getType(AmtReg);
410
411 auto &RB = *MRI.getRegBank(AmtReg);
412
413 auto NewExt = B.buildAnyExt(ExtAmtTy, AmtReg);
414 auto Mask = B.buildConstant(
416 auto And = B.buildAnd(ExtAmtTy, NewExt, Mask);
417 B.buildInstr(ShOpc, {ShDst}, {ShSrc, And});
418
419 MRI.setRegBank(NewExt.getReg(0), RB);
420 MRI.setRegBank(Mask.getReg(0), RB);
421 MRI.setRegBank(And.getReg(0), RB);
422 MI.eraseFromParent();
423}
424
425bool AMDGPURegBankCombinerImpl::combineD16Load(MachineInstr &MI) const {
426 Register Dst;
427 MachineInstr *Load;
428 GSExtLoad *SextLoad;
429 GZExtLoad *ZextLoad;
430 const MachineMemOperand *MMO;
431 const int64_t CleanLo16 = 0xFFFFFFFFFFFF0000;
432 const int64_t CleanHi16 = 0x000000000000FFFF;
433
434 auto TryApplyD16Load = [&](unsigned D16Opc, GAnyLoad *SmallLoad) {
435 LLT DstTy = MRI.getType(Dst);
436 if (DstTy != LLT::fixed_vector(2, LLT::integer(16)) &&
437 DstTy != LLT::fixed_vector(2, LLT::float16()))
438 return false;
439
440 MachineInstr *DstDef = MRI.getVRegDef(Dst);
441 MachineInstr *InsertPt = SmallLoad;
442 if (SmallLoad->isSimple() && isObviouslySafeToFold(*SmallLoad, MI))
443 InsertPt = &MI;
444 else if (!Helper.dominates(*DstDef, *SmallLoad))
445 return false;
446
447 applyD16Load(D16Opc, MI, SmallLoad, Dst, *InsertPt);
448 return true;
449 };
450
451 // Load lo
452 if (mi_match(MI.getOperand(1).getReg(), MRI,
454 m_Copy(m_SpecificICst(CleanLo16))),
455 m_OneUse(m_MInstr(Load)))))) {
456 if (mi_match(Load, MRI, m_GZExtLoad(ZextLoad, m_MMO(MMO)))) {
457 unsigned LoadSize = MMO->getSizeInBits().getValue();
458 if (LoadSize == 8)
459 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO_U8, ZextLoad);
460 if (LoadSize == 16)
461 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO, ZextLoad);
462
463 return false;
464 }
465
466 // s32 Load_lo16 holds SextLoad i8, Load_hi16 is zero.
467 // fake16: and (sextload i8 -> s32), 0xFFFF
468 // true16: zext (sextload i8 -> s16) -> s32
469 if (mi_match(Load, MRI,
470 m_any_of(m_GAnd(m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO))),
471 m_Copy(m_SpecificICst(CleanHi16))),
474 m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO)))))))) {
475 unsigned LoadSize = MMO->getSizeInBits().getValue();
476 if (LoadSize == 8)
477 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO_I8, SextLoad);
478 }
479
480 return false;
481 }
482
483 // Load hi
484 if (mi_match(MI.getOperand(1).getReg(), MRI,
486 m_Copy(m_SpecificICst(CleanHi16))),
488 m_Copy(m_SpecificICst(16)))))))) {
489 if (mi_match(Load, MRI, m_GZExtLoad(ZextLoad, m_MMO(MMO)))) {
490 unsigned LoadSize = MMO->getSizeInBits().getValue();
491 if (LoadSize == 8)
492 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI_U8, ZextLoad);
493 if (LoadSize == 16)
494 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI, ZextLoad);
495
496 return false;
497 }
498
499 // The shift discards the upper half of the sign-extending load.
500 if (mi_match(Load, MRI,
501 m_any_of(m_GSExtLoad(SextLoad, m_MMO(MMO)),
502 m_GAnd(m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO))),
503 m_Copy(m_SpecificICst(CleanHi16))),
506 m_OneUse(m_GSExtLoad(SextLoad, m_MMO(MMO)))))))) {
507 unsigned LoadSize = MMO->getSizeInBits().getValue();
508 if (LoadSize == 8)
509 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI_I8, SextLoad);
510 }
511
512 return false;
513 }
514
515 return false;
516}
517
518void AMDGPURegBankCombinerImpl::applyMinMaxToMinMax3(
519 MachineInstr &MI, MinMaxToMinMax3MatchInfo &MatchInfo) const {
520 B.buildInstr(MatchInfo.Opc, {MI.getOperand(0)},
521 {MatchInfo.Val0, MatchInfo.Val1, MatchInfo.Val2}, MI.getFlags());
522 MI.eraseFromParent();
523 return;
524}
525
526// min(min(a, b), c) == min(a, min(b, c)) == min3(a, b, c)
527// supported scalar type: S32 S16 U32 U16 F32 F16
528bool AMDGPURegBankCombinerImpl::matchMinMaxToMinMax3(
529 MachineInstr &MI, MinMaxToMinMax3MatchInfo &MatchInfo) const {
530 Register Dst = MI.getOperand(0).getReg();
531 Register Src1 = MI.getOperand(1).getReg();
532 Register Src2 = MI.getOperand(2).getReg();
533 // If the register is SGPR, don't optimize it.
534 if (!(isVgprRegBank(Dst) && isVgprRegBank(Src1) && isVgprRegBank(Src2))) {
535 return false;
536 }
537
538 LLT Ty = MRI.getType(Dst);
539 unsigned Opc = MI.getOpcode();
540 if (!(Ty == LLT::scalar(32) ||
541 (Ty == LLT::scalar(16) && STI.hasMin3Max3_16())))
542 return false;
543
544 Register R0, R1, R2;
545 if (!mi_match(MI, MRI,
547 m_OneUse(m_BinOp(Opc, m_Reg(R0), m_Reg(R1))),
548 m_Reg(R2)))) {
549 return false;
550 }
551
552 unsigned AMDGPUOpc = 0;
553 switch (Opc) {
554 case AMDGPU::G_SMAX:
555 AMDGPUOpc = AMDGPU::G_AMDGPU_SMAX3;
556 break;
557 case AMDGPU::G_SMIN:
558 AMDGPUOpc = AMDGPU::G_AMDGPU_SMIN3;
559 break;
560 case AMDGPU::G_UMAX:
561 AMDGPUOpc = AMDGPU::G_AMDGPU_UMAX3;
562 break;
563 case AMDGPU::G_UMIN:
564 AMDGPUOpc = AMDGPU::G_AMDGPU_UMIN3;
565 break;
566 case AMDGPU::G_FMAXNUM:
567 case AMDGPU::G_FMAXNUM_IEEE:
568 case AMDGPU::G_FMAXIMUMNUM:
569 AMDGPUOpc = AMDGPU::G_AMDGPU_FMAX3;
570 break;
571 case AMDGPU::G_FMINNUM:
572 case AMDGPU::G_FMINNUM_IEEE:
573 case AMDGPU::G_FMINIMUMNUM:
574 AMDGPUOpc = AMDGPU::G_AMDGPU_FMIN3;
575 break;
576 case AMDGPU::G_FMAXIMUM:
577 AMDGPUOpc = AMDGPU::G_AMDGPU_FMAXIMUM3;
578 break;
579 case AMDGPU::G_FMINIMUM:
580 AMDGPUOpc = AMDGPU::G_AMDGPU_FMINIMUM3;
581 break;
582 default:
583 return false;
584 }
585
586 MatchInfo = {AMDGPUOpc, R0, R1, R2};
587 return true;
588}
589
590void AMDGPURegBankCombinerImpl::applyD16Load(unsigned D16Opc,
591 MachineInstr &DstMI,
592 MachineInstr *SmallLoad,
593 Register SrcReg32ToOverwriteD16,
594 MachineInstr &InsertPt) const {
595 Register DstReg = DstMI.getOperand(0).getReg();
596 LLT SrcTy = MRI.getType(SrcReg32ToOverwriteD16);
597
598 // Dst and Src for D16 load need to have same type.
599 Register D16Dst =
600 SrcTy == MRI.getType(DstReg)
601 ? DstReg
602 : MRI.createVirtualRegister({MRI.getRegBank(DstReg), SrcTy});
603
604 B.setInstrAndDebugLoc(InsertPt);
605 B.setDebugLoc(SmallLoad->getDebugLoc());
606 B.buildInstr(D16Opc, {D16Dst},
607 {SmallLoad->getOperand(1).getReg(), SrcReg32ToOverwriteD16})
608 .setMemRefs(SmallLoad->memoperands());
609
610 if (D16Dst != DstReg)
611 B.buildBitcast(DstReg, D16Dst);
612
613 // Recursive cleanup may erase SmallLoad.
614 Register LoadDst = SmallLoad->getOperand(0).getReg();
615 eraseInstr(DstMI, MRI);
616 // Volatile and ordered atomic loads are not trivially dead.
617 if (MachineInstr *OldLoad = MRI.getVRegDef(LoadDst))
618 eraseInstr(*OldLoad, MRI);
619}
620
621SIModeRegisterDefaults AMDGPURegBankCombinerImpl::getMode() const {
622 return MF.getInfo<SIMachineFunctionInfo>()->getMode();
623}
624
625bool AMDGPURegBankCombinerImpl::getIEEE() const { return getMode().IEEE; }
626
627bool AMDGPURegBankCombinerImpl::getDX10Clamp() const {
628 return getMode().DX10Clamp;
629}
630
631bool AMDGPURegBankCombinerImpl::isFminnumIeee(const MachineInstr &MI) const {
632 return MI.getOpcode() == AMDGPU::G_FMINNUM_IEEE;
633}
634
635bool AMDGPURegBankCombinerImpl::isFminnum(const MachineInstr &MI) const {
636 return MI.getOpcode() == AMDGPU::G_FMINNUM;
637}
638
639bool AMDGPURegBankCombinerImpl::isFminnumLike(const MachineInstr &MI) const {
640 return isFminnumIeee(MI) || isFminnum(MI);
641}
642
643bool AMDGPURegBankCombinerImpl::isFCst(MachineInstr *MI) const {
644 return MI->getOpcode() == AMDGPU::G_FCONSTANT;
645}
646
647bool AMDGPURegBankCombinerImpl::isClampZeroToOne(MachineInstr *K0,
648 MachineInstr *K1) const {
649 if (isFCst(K0) && isFCst(K1)) {
650 const ConstantFP *KO_FPImm = K0->getOperand(1).getFPImm();
651 const ConstantFP *K1_FPImm = K1->getOperand(1).getFPImm();
652 return (KO_FPImm->isPosZero() && K1_FPImm->isOne()) ||
653 (KO_FPImm->isOne() && K1_FPImm->isPosZero());
654 }
655 return false;
656}
657
658static bool runCombiner(MachineFunction &MF,
659 function_ref<GISelValueTracking *()> GetVT,
660 function_ref<MachineDominatorTree *()> GetMDT,
661 bool EnableOpt) {
662 AMDGPURegBankCombinerImplRuleConfig RuleConfig;
663 if (!RuleConfig.parseCommandLineOption())
664 reportFatalUsageError("Invalid rule identifier");
665
666 if (MF.getProperties().hasFailedISel())
667 return false;
668
669 const Function &F = MF.getFunction();
670 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
671 const auto *LI = ST.getLegalizerInfo();
672
673 CombinerInfo CInfo(/*AllowIllegalOps=*/false, /*ShouldLegalizeIllegal=*/true,
674 LI, EnableOpt, F.hasOptSize(), F.hasMinSize());
675 // Disable fixed-point iteration to reduce compile-time
676 CInfo.MaxIterations = 1;
677 CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
678 // RegBankSelect seems not to leave dead instructions, so a full DCE pass is
679 // unnecessary.
680 CInfo.EnableFullDCE = false;
681
682 GISelValueTracking *VT = GetVT();
683 MachineDominatorTree *MDT = GetMDT();
684 AMDGPURegBankCombinerImpl Impl(MF, CInfo, *VT, /*CSEInfo=*/nullptr,
685 RuleConfig, ST, MDT, LI);
686 return Impl.combineMachineInstrs();
687}
688
689// Pass boilerplate
690// ================
691
692class AMDGPURegBankCombinerLegacy : public MachineFunctionPass {
693public:
694 static char ID;
695
696 AMDGPURegBankCombinerLegacy(bool IsOptLevelNone = false)
697 : MachineFunctionPass(ID), IsOptLevelNone(IsOptLevelNone) {}
698
699 StringRef getPassName() const override { return "AMDGPURegBankCombiner"; }
700
701 bool runOnMachineFunction(MachineFunction &MF) override;
702
703 void getAnalysisUsage(AnalysisUsage &AU) const override;
704
705private:
706 bool IsOptLevelNone;
707};
708} // end anonymous namespace
709
710void AMDGPURegBankCombinerLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
711 AU.setPreservesCFG();
713 AU.addRequired<GISelValueTrackingAnalysisLegacy>();
714 AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
715 if (!IsOptLevelNone) {
716 AU.addRequired<MachineDominatorTreeWrapperPass>();
717 }
719}
720
721bool AMDGPURegBankCombinerLegacy::runOnMachineFunction(MachineFunction &MF) {
722 const Function &F = MF.getFunction();
723 bool EnableOpt =
724 MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
725
726 return runCombiner(
727 MF,
728 [&]() {
729 return &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
730 },
731 [&]() -> MachineDominatorTree * {
732 return IsOptLevelNone ? nullptr
733 : &getAnalysis<MachineDominatorTreeWrapperPass>()
734 .getDomTree();
735 },
736 EnableOpt);
737}
738
739char AMDGPURegBankCombinerLegacy::ID = 0;
740INITIALIZE_PASS_BEGIN(AMDGPURegBankCombinerLegacy, DEBUG_TYPE,
741 "Combine AMDGPU machine instrs after regbankselect",
742 false, false)
744INITIALIZE_PASS_END(AMDGPURegBankCombinerLegacy, DEBUG_TYPE,
745 "Combine AMDGPU machine instrs after regbankselect", false,
746 false)
747
749 return new AMDGPURegBankCombinerLegacy(IsOptLevelNone);
750}
751
753 : IsOptLevelNone(IsOptLevelNone) {}
754
758 const Function &F = MF.getFunction();
759 bool EnableOpt = MF.getTarget().getOptLevel() != CodeGenOptLevel::None &&
760 !F.hasOptNone() && !shouldSkipOptimizationForOptBisect(F);
761
762 if (!runCombiner(
763 MF, [&]() { return &MFAM.getResult<GISelValueTrackingAnalysis>(MF); },
764 [&]() -> MachineDominatorTree * {
765 return IsOptLevelNone
766 ? nullptr
768 },
769 EnableOpt))
770 return PreservedAnalyses::all();
771
775 return PA;
776}
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.
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:880