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