LLVM 22.0.0git
SPIRVCombinerHelper.cpp
Go to the documentation of this file.
1//===-- SPIRVCombinerHelper.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
12#include "llvm/IR/IntrinsicsSPIRV.h"
14
15using namespace llvm;
16using namespace MIPatternMatch;
17
23
24/// This match is part of a combine that
25/// rewrites length(X - Y) to distance(X, Y)
26/// (f32 (g_intrinsic length
27/// (g_fsub (vXf32 X) (vXf32 Y))))
28/// ->
29/// (f32 (g_intrinsic distance
30/// (vXf32 X) (vXf32 Y)))
31///
33 if (MI.getOpcode() != TargetOpcode::G_INTRINSIC ||
34 cast<GIntrinsic>(MI).getIntrinsicID() != Intrinsic::spv_length)
35 return false;
36
37 // First operand of MI is `G_INTRINSIC` so start at operand 2.
38 Register SubReg = MI.getOperand(2).getReg();
39 MachineInstr *SubInstr = MRI.getVRegDef(SubReg);
40 if (SubInstr->getOpcode() != TargetOpcode::G_FSUB)
41 return false;
42
43 return true;
44}
45
47 // Extract the operands for X and Y from the match criteria.
48 Register SubDestReg = MI.getOperand(2).getReg();
49 MachineInstr *SubInstr = MRI.getVRegDef(SubDestReg);
50 Register SubOperand1 = SubInstr->getOperand(1).getReg();
51 Register SubOperand2 = SubInstr->getOperand(2).getReg();
52 Register ResultReg = MI.getOperand(0).getReg();
53
54 Builder.setInstrAndDebugLoc(MI);
55 Builder.buildIntrinsic(Intrinsic::spv_distance, ResultReg)
56 .addUse(SubOperand1)
57 .addUse(SubOperand2);
58
59 MI.eraseFromParent();
60}
61
62/// This match is part of a combine that
63/// rewrites select(fcmp(dot(I, Ng), 0), N, -N) to faceforward(N, I, Ng)
64/// (vXf32 (g_select
65/// (g_fcmp
66/// (g_intrinsic dot(vXf32 I) (vXf32 Ng)
67/// 0)
68/// (vXf32 N)
69/// (vXf32 g_fneg (vXf32 N))))
70/// ->
71/// (vXf32 (g_intrinsic faceforward
72/// (vXf32 N) (vXf32 I) (vXf32 Ng)))
73///
74/// This only works for Vulkan shader targets.
75///
77 if (!STI.isShader())
78 return false;
79
80 // Match overall select pattern.
81 Register CondReg, TrueReg, FalseReg;
82 if (!mi_match(MI.getOperand(0).getReg(), MRI,
83 m_GISelect(m_Reg(CondReg), m_Reg(TrueReg), m_Reg(FalseReg))))
84 return false;
85
86 // Match the FCMP condition.
87 Register DotReg, CondZeroReg;
89 if (!mi_match(CondReg, MRI,
90 m_GFCmp(m_Pred(Pred), m_Reg(DotReg), m_Reg(CondZeroReg))) ||
91 !(Pred == CmpInst::FCMP_OLT || Pred == CmpInst::FCMP_ULT)) {
92 if (!(Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT))
93 return false;
94 std::swap(DotReg, CondZeroReg);
95 }
96
97 // Check if FCMP is a comparison between a dot product and 0.
98 MachineInstr *DotInstr = MRI.getVRegDef(DotReg);
99 if (DotInstr->getOpcode() != TargetOpcode::G_INTRINSIC ||
100 cast<GIntrinsic>(DotInstr)->getIntrinsicID() != Intrinsic::spv_fdot) {
101 Register DotOperand1, DotOperand2;
102 // Check for scalar dot product.
103 if (!mi_match(DotReg, MRI,
104 m_GFMul(m_Reg(DotOperand1), m_Reg(DotOperand2))) ||
105 !MRI.getType(DotOperand1).isScalar() ||
106 !MRI.getType(DotOperand2).isScalar())
107 return false;
108 }
109
110 const ConstantFP *ZeroVal;
111 if (!mi_match(CondZeroReg, MRI, m_GFCst(ZeroVal)) || !ZeroVal->isZero())
112 return false;
113
114 // Check if select's false operand is the negation of the true operand.
115 auto AreNegatedConstantsOrSplats = [&](Register TrueReg, Register FalseReg) {
116 std::optional<FPValueAndVReg> TrueVal, FalseVal;
117 if (!mi_match(TrueReg, MRI, m_GFCstOrSplat(TrueVal)) ||
118 !mi_match(FalseReg, MRI, m_GFCstOrSplat(FalseVal)))
119 return false;
120 APFloat TrueValNegated = TrueVal->Value;
121 TrueValNegated.changeSign();
122 return FalseVal->Value.compare(TrueValNegated) == APFloat::cmpEqual;
123 };
124
125 if (!mi_match(TrueReg, MRI, m_GFNeg(m_SpecificReg(FalseReg))) &&
126 !mi_match(FalseReg, MRI, m_GFNeg(m_SpecificReg(TrueReg)))) {
127 std::optional<FPValueAndVReg> MulConstant;
128 MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
129 MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);
130 if (TrueInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&
131 FalseInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&
132 TrueInstr->getNumOperands() == FalseInstr->getNumOperands()) {
133 for (unsigned I = 1; I < TrueInstr->getNumOperands(); ++I)
134 if (!AreNegatedConstantsOrSplats(TrueInstr->getOperand(I).getReg(),
135 FalseInstr->getOperand(I).getReg()))
136 return false;
137 } else if (mi_match(TrueReg, MRI,
138 m_GFMul(m_SpecificReg(FalseReg),
139 m_GFCstOrSplat(MulConstant))) ||
140 mi_match(FalseReg, MRI,
141 m_GFMul(m_SpecificReg(TrueReg),
142 m_GFCstOrSplat(MulConstant))) ||
143 mi_match(TrueReg, MRI,
144 m_GFMul(m_GFCstOrSplat(MulConstant),
145 m_SpecificReg(FalseReg))) ||
146 mi_match(FalseReg, MRI,
147 m_GFMul(m_GFCstOrSplat(MulConstant),
148 m_SpecificReg(TrueReg)))) {
149 if (!MulConstant || !MulConstant->Value.isExactlyValue(-1.0))
150 return false;
151 } else if (!AreNegatedConstantsOrSplats(TrueReg, FalseReg))
152 return false;
153 }
154
155 return true;
156}
157
159 // Extract the operands for N, I, and Ng from the match criteria.
160 Register CondReg = MI.getOperand(1).getReg();
161 MachineInstr *CondInstr = MRI.getVRegDef(CondReg);
162 Register DotReg = CondInstr->getOperand(2).getReg();
163 CmpInst::Predicate Pred = cast<GFCmp>(CondInstr)->getCond();
164 if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)
165 DotReg = CondInstr->getOperand(3).getReg();
166 MachineInstr *DotInstr = MRI.getVRegDef(DotReg);
167 Register DotOperand1, DotOperand2;
168 if (DotInstr->getOpcode() == TargetOpcode::G_FMUL) {
169 DotOperand1 = DotInstr->getOperand(1).getReg();
170 DotOperand2 = DotInstr->getOperand(2).getReg();
171 } else {
172 DotOperand1 = DotInstr->getOperand(2).getReg();
173 DotOperand2 = DotInstr->getOperand(3).getReg();
174 }
175 Register TrueReg = MI.getOperand(2).getReg();
176 Register FalseReg = MI.getOperand(3).getReg();
177 MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
178 if (TrueInstr->getOpcode() == TargetOpcode::G_FNEG ||
179 TrueInstr->getOpcode() == TargetOpcode::G_FMUL)
180 std::swap(TrueReg, FalseReg);
181 MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);
182
183 Register ResultReg = MI.getOperand(0).getReg();
184 Builder.setInstrAndDebugLoc(MI);
185 Builder.buildIntrinsic(Intrinsic::spv_faceforward, ResultReg)
186 .addUse(TrueReg) // N
187 .addUse(DotOperand1) // I
188 .addUse(DotOperand2); // Ng
189
191 MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();
192 auto RemoveAllUses = [&](Register Reg) {
194 for (auto &UseMI : MRI.use_instructions(Reg))
195 UsesToErase.push_back(&UseMI);
196
197 // calling eraseFromParent to early invalidates the iterator.
198 for (auto *MIToErase : UsesToErase)
199 MIToErase->eraseFromParent();
200 };
201
202 RemoveAllUses(CondReg); // remove all uses of FCMP Result
203 GR->invalidateMachineInstr(CondInstr);
204 CondInstr->eraseFromParent(); // remove FCMP instruction
205 RemoveAllUses(DotReg); // remove all uses of spv_fdot/G_FMUL Result
206 GR->invalidateMachineInstr(DotInstr);
207 DotInstr->eraseFromParent(); // remove spv_fdot/G_FMUL instruction
208 RemoveAllUses(FalseReg);
209 GR->invalidateMachineInstr(FalseInstr);
210 FalseInstr->eraseFromParent();
211}
unsigned SubReg
MachineInstrBuilder & UseMI
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Declares convenience wrapper classes for interpreting MachineInstr instances as specific generic oper...
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
Contains matchers for matching SSA Machine Instructions.
void changeSign()
Definition APFloat.h:1279
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:676
@ FCMP_OLT
0 1 0 0 True if ordered and less than
Definition InstrTypes.h:682
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
Definition InstrTypes.h:680
@ FCMP_ULT
1 1 0 0 True if unordered or less than
Definition InstrTypes.h:690
@ FCMP_UGT
1 0 1 0 True if unordered or greater than
Definition InstrTypes.h:688
MachineRegisterInfo & MRI
const LegalizerInfo * LI
MachineDominatorTree * MDT
GISelValueTracking * VT
GISelChangeObserver & Observer
MachineIRBuilder & Builder
ConstantFP - Floating Point Values [float, double].
Definition Constants.h:282
bool isZero() const
Return true if the value is positive or negative zero.
Definition Constants.h:329
Abstract class that contains various methods for clients to notify about changes.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
Helper class to build MachineInstr.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
unsigned getNumOperands() const
Retuns the total number of operands.
LLVM_ABI void eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
const MachineOperand & getOperand(unsigned i) const
Register getReg() const
getReg - Returns the register number.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
bool matchSelectToFaceForward(MachineInstr &MI) const
This match is part of a combine that rewrites select(fcmp(dot(I, Ng), 0), N, -N) to faceforward(N,...
CombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B, bool IsPreLegalize, GISelValueTracking *VT=nullptr, MachineDominatorTree *MDT=nullptr, const LegalizerInfo *LI=nullptr)
void applySPIRVFaceForward(MachineInstr &MI) const
SPIRVCombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B, bool IsPreLegalize, GISelValueTracking *VT, MachineDominatorTree *MDT, const LegalizerInfo *LI, const SPIRVSubtarget &STI)
const SPIRVSubtarget & STI
void applySPIRVDistance(MachineInstr &MI) const
bool matchLengthToDistance(MachineInstr &MI) const
This match is part of a combine that rewrites length(X - Y) to distance(X, Y) (f32 (g_intrinsic lengt...
void invalidateMachineInstr(MachineInstr *MI)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
operand_type_match m_Reg()
operand_type_match m_Pred()
TernaryOp_match< Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT > m_GISelect(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2)
bool mi_match(Reg R, const MachineRegisterInfo &MRI, Pattern &&P)
SpecificRegisterMatch m_SpecificReg(Register RequestedReg)
Matches a register only if it is equal to RequestedReg.
UnaryOp_match< SrcTy, TargetOpcode::G_FNEG > m_GFNeg(const SrcTy &Src)
GFCstAndRegMatch m_GFCst(std::optional< FPValueAndVReg > &FPValReg)
GFCstOrSplatGFCstMatch m_GFCstOrSplat(std::optional< FPValueAndVReg > &FPValReg)
BinaryOp_match< LHS, RHS, TargetOpcode::G_FMUL, true > m_GFMul(const LHS &L, const RHS &R)
CompareOp_match< Pred, LHS, RHS, TargetOpcode::G_FCMP > m_GFCmp(const Pred &P, const LHS &L, const RHS &R)
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:872