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 MachineInstr *DstDef = MRI.getVRegDef(Dst);
436 MachineInstr *InsertPt = SmallLoad;
437 if (SmallLoad->isSimple() && isObviouslySafeToFold(*SmallLoad, MI))
438 InsertPt = &MI;
439 else if (!Helper.dominates(*DstDef, *SmallLoad))
440 return false;
441
442 applyD16Load(D16Opc, MI, SmallLoad, Dst, *InsertPt);
443 return true;
444 };
445
446 // Load lo
447 if (mi_match(MI.getOperand(1).getReg(), MRI,
449 m_Copy(m_SpecificICst(CleanLo16))),
451 if (mi_match(Load, MRI, m_GZExtLoad(ZextLoad, m_MMO(MMO)))) {
452 unsigned LoadSize = MMO->getSizeInBits().getValue();
453 if (LoadSize == 8)
454 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO_U8, ZextLoad);
455 if (LoadSize == 16)
456 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO, ZextLoad);
457
458 return false;
459 }
460
461 // s32 Load_lo16 holds SextLoad i8, Load_hi16 is zero.
462 // fake16: and (sextload i8 -> s32), 0xFFFF
463 // true16: zext (sextload i8 -> s16) -> s32
464 if (mi_match(
465 Load, MRI,
467 m_Copy(m_SpecificICst(CleanHi16))),
470 SextLoad, m_MMO(MMO)))))))) {
471 unsigned LoadSize = MMO->getSizeInBits().getValue();
472 if (LoadSize == 8)
473 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_LO_I8, SextLoad);
474 }
475
476 return false;
477 }
478
479 // Load hi
480 if (mi_match(
481 MI.getOperand(1).getReg(), MRI,
483 m_GAnd(m_GBitcast(m_Reg(Dst)), m_Copy(m_SpecificICst(CleanHi16))),
485 m_Copy(m_SpecificICst(16)))))))) {
486 if (mi_match(Load, MRI, m_GZExtLoad(ZextLoad, m_MMO(MMO)))) {
487 unsigned LoadSize = MMO->getSizeInBits().getValue();
488 if (LoadSize == 8)
489 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI_U8, ZextLoad);
490 if (LoadSize == 16)
491 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI, ZextLoad);
492
493 return false;
494 }
495
496 // s32 Load_lo16 holds SextLoad i8, Load_hi16 is zero.
497 // fake16: and (sextload i8 -> s32), 0xFFFF
498 // true16: zext (sextload i8 -> s16) -> s32
499 if (mi_match(
500 Load, MRI,
502 m_Copy(m_SpecificICst(CleanHi16))),
505 SextLoad, m_MMO(MMO)))))))) {
506 unsigned LoadSize = MMO->getSizeInBits().getValue();
507 if (LoadSize == 8)
508 return TryApplyD16Load(AMDGPU::G_AMDGPU_LOAD_D16_HI_I8, SextLoad);
509 }
510
511 return false;
512 }
513
514 return false;
515}
516
517void AMDGPURegBankCombinerImpl::applyMinMaxToMinMax3(
518 MachineInstr &MI, MinMaxToMinMax3MatchInfo &MatchInfo) const {
519 B.buildInstr(MatchInfo.Opc, {MI.getOperand(0)},
520 {MatchInfo.Val0, MatchInfo.Val1, MatchInfo.Val2}, MI.getFlags());
521 MI.eraseFromParent();
522 return;
523}
524
525// min(min(a, b), c) == min(a, min(b, c)) == min3(a, b, c)
526// supported scalar type: S32 S16 U32 U16 F32 F16
527bool AMDGPURegBankCombinerImpl::matchMinMaxToMinMax3(
528 MachineInstr &MI, MinMaxToMinMax3MatchInfo &MatchInfo) const {
529 Register Dst = MI.getOperand(0).getReg();
530 Register Src1 = MI.getOperand(1).getReg();
531 Register Src2 = MI.getOperand(2).getReg();
532 // If the register is SGPR, don't optimize it.
533 if (!(isVgprRegBank(Dst) && isVgprRegBank(Src1) && isVgprRegBank(Src2))) {
534 return false;
535 }
536
537 LLT Ty = MRI.getType(Dst);
538 unsigned Opc = MI.getOpcode();
539 if (!(Ty == LLT::scalar(32) ||
540 (Ty == LLT::scalar(16) && STI.hasMin3Max3_16())))
541 return false;
542
543 Register R0, R1, R2;
544 if (!mi_match(MI, MRI,
547 m_Reg(R2)))) {
548 return false;
549 }
550
551 unsigned AMDGPUOpc = 0;
552 switch (Opc) {
553 case AMDGPU::G_SMAX:
554 AMDGPUOpc = AMDGPU::G_AMDGPU_SMAX3;
555 break;
556 case AMDGPU::G_SMIN:
557 AMDGPUOpc = AMDGPU::G_AMDGPU_SMIN3;
558 break;
559 case AMDGPU::G_UMAX:
560 AMDGPUOpc = AMDGPU::G_AMDGPU_UMAX3;
561 break;
562 case AMDGPU::G_UMIN:
563 AMDGPUOpc = AMDGPU::G_AMDGPU_UMIN3;
564 break;
565 case AMDGPU::G_FMAXNUM:
566 case AMDGPU::G_FMAXNUM_IEEE:
567 AMDGPUOpc = AMDGPU::G_AMDGPU_FMAX3;
568 break;
569 case AMDGPU::G_FMINNUM:
570 case AMDGPU::G_FMINNUM_IEEE:
571 AMDGPUOpc = AMDGPU::G_AMDGPU_FMIN3;
572 break;
573 case AMDGPU::G_FMAXIMUM:
574 case AMDGPU::G_FMAXIMUMNUM:
575 AMDGPUOpc = AMDGPU::G_AMDGPU_FMAXIMUM3;
576 break;
577 case AMDGPU::G_FMINIMUM:
578 case AMDGPU::G_FMINIMUMNUM:
579 AMDGPUOpc = AMDGPU::G_AMDGPU_FMINIMUM3;
580 break;
581 default:
582 return false;
583 }
584
585 MatchInfo = {AMDGPUOpc, R0, R1, R2};
586 return true;
587}
588
589void AMDGPURegBankCombinerImpl::applyD16Load(unsigned D16Opc,
590 MachineInstr &DstMI,
591 MachineInstr *SmallLoad,
592 Register SrcReg32ToOverwriteD16,
593 MachineInstr &InsertPt) const {
594 Register DstReg = DstMI.getOperand(0).getReg();
595 LLT SrcTy = MRI.getType(SrcReg32ToOverwriteD16);
596
597 // Dst and Src for D16 load need to have same type.
598 Register D16Dst =
599 SrcTy == MRI.getType(DstReg)
600 ? DstReg
601 : MRI.createVirtualRegister({MRI.getRegBank(DstReg), SrcTy});
602
603 B.setInstrAndDebugLoc(InsertPt);
604 B.setDebugLoc(SmallLoad->getDebugLoc());
605 B.buildInstr(D16Opc, {D16Dst},
606 {SmallLoad->getOperand(1).getReg(), SrcReg32ToOverwriteD16})
607 .setMemRefs(SmallLoad->memoperands());
608
609 if (D16Dst != DstReg)
610 B.buildBitcast(DstReg, D16Dst);
611
612 // Recursive cleanup may erase SmallLoad.
613 Register LoadDst = SmallLoad->getOperand(0).getReg();
614 eraseInstr(DstMI, MRI);
615 // Volatile and ordered atomic loads are not trivially dead.
616 if (MachineInstr *OldLoad = MRI.getVRegDef(LoadDst))
617 eraseInstr(*OldLoad, MRI);
618}
619
620SIModeRegisterDefaults AMDGPURegBankCombinerImpl::getMode() const {
621 return MF.getInfo<SIMachineFunctionInfo>()->getMode();
622}
623
624bool AMDGPURegBankCombinerImpl::getIEEE() const { return getMode().IEEE; }
625
626bool AMDGPURegBankCombinerImpl::getDX10Clamp() const {
627 return getMode().DX10Clamp;
628}
629
630bool AMDGPURegBankCombinerImpl::isFminnumIeee(const MachineInstr &MI) const {
631 return MI.getOpcode() == AMDGPU::G_FMINNUM_IEEE;
632}
633
634bool AMDGPURegBankCombinerImpl::isFminnum(const MachineInstr &MI) const {
635 return MI.getOpcode() == AMDGPU::G_FMINNUM;
636}
637
638bool AMDGPURegBankCombinerImpl::isFminnumLike(const MachineInstr &MI) const {
639 return isFminnumIeee(MI) || isFminnum(MI);
640}
641
642bool AMDGPURegBankCombinerImpl::isFCst(MachineInstr *MI) const {
643 return MI->getOpcode() == AMDGPU::G_FCONSTANT;
644}
645
646bool AMDGPURegBankCombinerImpl::isClampZeroToOne(MachineInstr *K0,
647 MachineInstr *K1) const {
648 if (isFCst(K0) && isFCst(K1)) {
649 const ConstantFP *KO_FPImm = K0->getOperand(1).getFPImm();
650 const ConstantFP *K1_FPImm = K1->getOperand(1).getFPImm();
651 return (KO_FPImm->isPosZero() && K1_FPImm->isOne()) ||
652 (KO_FPImm->isOne() && K1_FPImm->isPosZero());
653 }
654 return false;
655}
656
657static bool runCombiner(MachineFunction &MF,
658 function_ref<GISelValueTracking *()> GetVT,
659 function_ref<MachineDominatorTree *()> GetMDT,
660 bool EnableOpt) {
661 AMDGPURegBankCombinerImplRuleConfig RuleConfig;
662 if (!RuleConfig.parseCommandLineOption())
663 reportFatalUsageError("Invalid rule identifier");
664
665 if (MF.getProperties().hasFailedISel())
666 return false;
667
668 const Function &F = MF.getFunction();
669 const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
670 const auto *LI = ST.getLegalizerInfo();
671
672 CombinerInfo CInfo(/*AllowIllegalOps=*/false, /*ShouldLegalizeIllegal=*/true,
673 LI, EnableOpt, F.hasOptSize(), F.hasMinSize());
674 // Disable fixed-point iteration to reduce compile-time
675 CInfo.MaxIterations = 1;
676 CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
677 // RegBankSelect seems not to leave dead instructions, so a full DCE pass is
678 // unnecessary.
679 CInfo.EnableFullDCE = false;
680
681 GISelValueTracking *VT = GetVT();
682 MachineDominatorTree *MDT = GetMDT();
683 AMDGPURegBankCombinerImpl Impl(MF, CInfo, *VT, /*CSEInfo=*/nullptr,
684 RuleConfig, ST, MDT, LI);
685 return Impl.combineMachineInstrs();
686}
687
688// Pass boilerplate
689// ================
690
691class AMDGPURegBankCombinerLegacy : public MachineFunctionPass {
692public:
693 static char ID;
694
695 AMDGPURegBankCombinerLegacy(bool IsOptLevelNone = false)
696 : MachineFunctionPass(ID), IsOptLevelNone(IsOptLevelNone) {}
697
698 StringRef getPassName() const override { return "AMDGPURegBankCombiner"; }
699
700 bool runOnMachineFunction(MachineFunction &MF) override;
701
702 void getAnalysisUsage(AnalysisUsage &AU) const override;
703
704private:
705 bool IsOptLevelNone;
706};
707} // end anonymous namespace
708
709void AMDGPURegBankCombinerLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
710 AU.setPreservesCFG();
712 AU.addRequired<GISelValueTrackingAnalysisLegacy>();
713 AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
714 if (!IsOptLevelNone) {
715 AU.addRequired<MachineDominatorTreeWrapperPass>();
716 }
718}
719
720bool AMDGPURegBankCombinerLegacy::runOnMachineFunction(MachineFunction &MF) {
721 const Function &F = MF.getFunction();
722 bool EnableOpt =
723 MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
724
725 return runCombiner(
726 MF,
727 [&]() {
728 return &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
729 },
730 [&]() -> MachineDominatorTree * {
731 return IsOptLevelNone ? nullptr
732 : &getAnalysis<MachineDominatorTreeWrapperPass>()
733 .getDomTree();
734 },
735 EnableOpt);
736}
737
738char AMDGPURegBankCombinerLegacy::ID = 0;
739INITIALIZE_PASS_BEGIN(AMDGPURegBankCombinerLegacy, DEBUG_TYPE,
740 "Combine AMDGPU machine instrs after regbankselect",
741 false, false)
743INITIALIZE_PASS_END(AMDGPURegBankCombinerLegacy, DEBUG_TYPE,
744 "Combine AMDGPU machine instrs after regbankselect", false,
745 false)
746
748 return new AMDGPURegBankCombinerLegacy(IsOptLevelNone);
749}
750
752 : IsOptLevelNone(IsOptLevelNone) {}
753
757 const Function &F = MF.getFunction();
758 bool EnableOpt = MF.getTarget().getOptLevel() != CodeGenOptLevel::None &&
759 !F.hasOptNone() && !shouldSkipOptimizationForOptBisect(F);
760
761 if (!runCombiner(
762 MF, [&]() { return &MFAM.getResult<GISelValueTrackingAnalysis>(MF); },
763 [&]() -> MachineDominatorTree * {
764 return IsOptLevelNone
765 ? nullptr
767 },
768 EnableOpt))
769 return PreservedAnalyses::all();
770
774 return PA;
775}
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".
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)
OneNonDBGUse_match< SubPat > m_OneNonDBGUse(const SubPat &SP)
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)
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:497
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:1137
@ And
Bitwise or logical AND of integers.
LLVM_ABI void eraseInstr(MachineInstr &MI, MachineRegisterInfo &MRI, LostDebugLocObserver *LocObserver=nullptr)
Definition Utils.cpp:1673
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