LLVM 24.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
10#include "SPIRVGlobalRegistry.h"
11#include "SPIRVUtils.h"
15#include "llvm/IR/IntrinsicsSPIRV.h"
16#include "llvm/IR/LLVMContext.h" // Explicitly include for LLVMContext
18
19using namespace llvm;
20using namespace MIPatternMatch;
21
27
28/// This match is part of a combine that
29/// rewrites length(X - Y) to distance(X, Y)
30/// (f32 (g_intrinsic length
31/// (g_fsub (vXf32 X) (vXf32 Y))))
32/// ->
33/// (f32 (g_intrinsic distance
34/// (vXf32 X) (vXf32 Y)))
35///
38 return false;
39
40 // First operand of MI is `G_INTRINSIC` so start at operand 2.
41 Register SubReg = MI.getOperand(2).getReg();
42 return mi_match(SubReg, MRI, m_GFSub(m_Reg(), m_Reg()));
43}
44
46 // Extract the operands for X and Y from the match criteria.
47 Register SubDestReg = MI.getOperand(2).getReg();
48 MachineInstr *SubInstr = MRI.getVRegDef(SubDestReg);
49 Register SubOperand1 = SubInstr->getOperand(1).getReg();
50 Register SubOperand2 = SubInstr->getOperand(2).getReg();
51 Register ResultReg = MI.getOperand(0).getReg();
52
53 Builder.setInstrAndDebugLoc(MI);
54 Builder.buildIntrinsic(Intrinsic::spv_distance, ResultReg)
55 .addUse(SubOperand1)
56 .addUse(SubOperand2);
57
58 MI.eraseFromParent();
59}
60
61/// This match is part of a combine that
62/// rewrites X / length(X) to normalize(X)
63/// (vXf32 (g_fdiv
64/// (vXf32 X)
65/// (vXf32 splat
66/// (f32 (g_intrinsic length (vXf32 X))))))
67/// ->
68/// (vXf32 (g_intrinsic normalize (vXf32 X)))
69///
71 Register NumeratorReg = MI.getOperand(1).getReg();
72 Register DivisorReg = MI.getOperand(2).getReg();
73
74 // Match the divisor as a splat of length, inserted into lane 0.
75 MachineInstr *ShuffleInstr = MRI.getVRegDef(DivisorReg);
76 if (ShuffleInstr->getOpcode() != TargetOpcode::G_SHUFFLE_VECTOR)
77 return false;
78 if (!all_of(cast<GShuffleVector>(ShuffleInstr)->getMask(),
79 [](int M) { return M == 0; }))
80 return false;
81
82 MachineInstr *InsertInstr =
83 MRI.getVRegDef(ShuffleInstr->getOperand(1).getReg());
84 if (!isSpvIntrinsic(*InsertInstr, Intrinsic::spv_insertelt))
85 return false;
86 if (!mi_match(InsertInstr->getOperand(4).getReg(), MRI, m_ZeroInt()))
87 return false;
88
89 MachineInstr *LengthInstr =
90 MRI.getVRegDef(InsertInstr->getOperand(3).getReg());
91 if (!isSpvIntrinsic(*LengthInstr, Intrinsic::spv_length))
92 return false;
93
94 // Check that length's argument is the same as the numerator.
95 return LengthInstr->getOperand(2).getReg() == NumeratorReg;
96}
97
99 // Extract the operand for X from the match criteria.
100 Register NumeratorReg = MI.getOperand(1).getReg();
101 Register ResultReg = MI.getOperand(0).getReg();
102
103 Builder.setInstrAndDebugLoc(MI);
104 Builder.buildIntrinsic(Intrinsic::spv_normalize, ResultReg)
105 .addUse(NumeratorReg);
106
107 MI.eraseFromParent();
108}
109
110/// This match is part of a combine that
111/// rewrites select(fcmp(dot(I, Ng), 0), N, -N) to faceforward(N, I, Ng)
112/// (vXf32 (g_select
113/// (g_fcmp
114/// (g_intrinsic dot(vXf32 I) (vXf32 Ng)
115/// 0)
116/// (vXf32 N)
117/// (vXf32 g_fneg (vXf32 N))))
118/// ->
119/// (vXf32 (g_intrinsic faceforward
120/// (vXf32 N) (vXf32 I) (vXf32 Ng)))
121///
122/// This only works for Vulkan shader targets.
123///
125 if (!STI.isShader())
126 return false;
127
128 // Match overall select pattern.
129 Register CondReg, TrueReg, FalseReg;
130 if (!mi_match(MI.getOperand(0).getReg(), MRI,
131 m_GISelect(m_Reg(CondReg), m_Reg(TrueReg), m_Reg(FalseReg))))
132 return false;
133
134 // Match the FCMP condition.
135 Register DotReg, CondZeroReg;
137 if (!mi_match(CondReg, MRI,
138 m_GFCmp(m_Pred(Pred), m_Reg(DotReg), m_Reg(CondZeroReg))))
139 return false;
140 if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)
141 std::swap(DotReg, CondZeroReg);
142 else if (!(Pred == CmpInst::FCMP_OLT || Pred == CmpInst::FCMP_ULT))
143 return false;
144
145 // Check if FCMP is a comparison between a dot product and 0.
147 Register DotOperand1, DotOperand2;
148 // Check for scalar dot product.
149 if (!mi_match(DotReg, MRI,
150 m_GFMul(m_Reg(DotOperand1), m_Reg(DotOperand2))) ||
151 !MRI.getType(DotOperand1).isScalar() ||
152 !MRI.getType(DotOperand2).isScalar())
153 return false;
154 }
155
156 const ConstantFP *ZeroVal;
157 if (!mi_match(CondZeroReg, MRI, m_GFCst(ZeroVal)) || !ZeroVal->isZero())
158 return false;
159
160 // Check if select's false operand is the negation of the true operand.
161 auto AreNegatedConstantsOrSplats = [&](Register TrueReg, Register FalseReg) {
162 std::optional<FPValueAndVReg> TrueVal, FalseVal;
163 if (!mi_match(TrueReg, MRI, m_GFCstOrSplat(TrueVal)) ||
164 !mi_match(FalseReg, MRI, m_GFCstOrSplat(FalseVal)))
165 return false;
166 APFloat TrueValNegated = TrueVal->Value;
167 TrueValNegated.changeSign();
168 return FalseVal->Value.compare(TrueValNegated) == APFloat::cmpEqual;
169 };
170
171 if (!mi_match(TrueReg, MRI, m_GFNeg(m_SpecificReg(FalseReg))) &&
172 !mi_match(FalseReg, MRI, m_GFNeg(m_SpecificReg(TrueReg)))) {
173 std::optional<FPValueAndVReg> MulConstant;
174 GBuildVector *TrueInstr, *FalseInstr;
175 if (mi_match(TrueReg, MRI, m_GBuildVector(TrueInstr)) &&
176 mi_match(FalseReg, MRI, m_GBuildVector(FalseInstr)) &&
177 TrueInstr->getNumOperands() == FalseInstr->getNumOperands()) {
178 for (unsigned I = 1; I < TrueInstr->getNumOperands(); ++I)
179 if (!AreNegatedConstantsOrSplats(TrueInstr->getOperand(I).getReg(),
180 FalseInstr->getOperand(I).getReg()))
181 return false;
182 } else if (mi_match(TrueReg, MRI,
183 m_GFMul(m_SpecificReg(FalseReg),
184 m_GFCstOrSplat(MulConstant))) ||
185 mi_match(FalseReg, MRI,
186 m_GFMul(m_SpecificReg(TrueReg),
187 m_GFCstOrSplat(MulConstant))) ||
188 mi_match(TrueReg, MRI,
189 m_GFMul(m_GFCstOrSplat(MulConstant),
190 m_SpecificReg(FalseReg))) ||
191 mi_match(FalseReg, MRI,
192 m_GFMul(m_GFCstOrSplat(MulConstant),
193 m_SpecificReg(TrueReg)))) {
194 if (!MulConstant || !MulConstant->Value.isMinusOne())
195 return false;
196 } else if (!AreNegatedConstantsOrSplats(TrueReg, FalseReg))
197 return false;
198 }
199
200 return true;
201}
202
204 // Extract the operands for N, I, and Ng from the match criteria.
205 Register CondReg = MI.getOperand(1).getReg();
206 MachineInstr *CondInstr = MRI.getVRegDef(CondReg);
207 Register DotReg = CondInstr->getOperand(2).getReg();
208 CmpInst::Predicate Pred = cast<GFCmp>(CondInstr)->getCond();
209 if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)
210 DotReg = CondInstr->getOperand(3).getReg();
211 MachineInstr *DotInstr = MRI.getVRegDef(DotReg);
212 Register DotOperand1, DotOperand2;
213 if (DotInstr->getOpcode() == TargetOpcode::G_FMUL) {
214 DotOperand1 = DotInstr->getOperand(1).getReg();
215 DotOperand2 = DotInstr->getOperand(2).getReg();
216 } else {
217 DotOperand1 = DotInstr->getOperand(2).getReg();
218 DotOperand2 = DotInstr->getOperand(3).getReg();
219 }
220 Register TrueReg = MI.getOperand(2).getReg();
221 Register FalseReg = MI.getOperand(3).getReg();
222 MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
223 if (TrueInstr->getOpcode() == TargetOpcode::G_FNEG ||
224 TrueInstr->getOpcode() == TargetOpcode::G_FMUL)
225 std::swap(TrueReg, FalseReg);
226
227 Register ResultReg = MI.getOperand(0).getReg();
228 Builder.setInstrAndDebugLoc(MI);
229 Builder.buildIntrinsic(Intrinsic::spv_faceforward, ResultReg)
230 .addUse(TrueReg) // N
231 .addUse(DotOperand1) // I
232 .addUse(DotOperand2); // Ng
233
234 MI.eraseFromParent();
235}
236
238 Register ResReg = MI.getOperand(0).getReg();
239 Register InReg = MI.getOperand(2).getReg();
240 uint32_t Rows = MI.getOperand(3).getImm();
241 uint32_t Cols = MI.getOperand(4).getImm();
242
243 Builder.setInstrAndDebugLoc(MI);
244
245 // A 1xN or Nx1 transpose is a pure reshape.
246 if (Rows == 1 || Cols == 1) {
247 Builder.buildCopy(ResReg, InReg);
248 MI.eraseFromParent();
249 return;
250 }
251
253 for (uint32_t K = 0; K < Rows * Cols; ++K) {
254 uint32_t R = K / Cols;
255 uint32_t C = K % Cols;
256 Mask.push_back(C * Rows + R);
257 }
258
259 Builder.buildShuffleVector(ResReg, InReg, InReg, Mask);
260 MI.eraseFromParent();
261}
262
264SPIRVCombinerHelper::extractColumns(Register MatrixReg, uint32_t NumberOfCols,
265 SPIRVTypeInst SpvColType,
266 SPIRVGlobalRegistry *GR) const {
267 // If the matrix is a single colunm, return that single column.
268 if (NumberOfCols == 1)
269 return {MatrixReg};
270
272 LLT ColTy = GR->getRegType(SpvColType);
273 for (uint32_t J = 0; J < NumberOfCols; ++J)
275 Builder.buildUnmerge(Cols, MatrixReg);
276 for (Register R : Cols) {
277 setRegClassType(R, SpvColType, GR, &MRI, Builder.getMF());
278 }
279 return Cols;
280}
281
283SPIRVCombinerHelper::extractRows(Register MatrixReg, uint32_t NumRows,
284 uint32_t NumCols, SPIRVTypeInst SpvRowType,
285 SPIRVGlobalRegistry *GR) const {
287 LLT VecTy = GR->getRegType(SpvRowType);
288
289 // If there is only one column, then each row is a scalar that needs
290 // to be extracted.
291 if (NumCols == 1) {
292 assert(!isVectorType(SpvRowType));
293 for (uint32_t I = 0; I < NumRows; ++I)
294 Rows.push_back(MRI.createGenericVirtualRegister(VecTy));
295 Builder.buildUnmerge(Rows, MatrixReg);
296 for (Register R : Rows) {
297 setRegClassType(R, SpvRowType, GR, &MRI, Builder.getMF());
298 }
299 return Rows;
300 }
301
302 // If the matrix is a single row return that row.
303 if (NumRows == 1) {
304 return {MatrixReg};
305 }
306
307 for (uint32_t I = 0; I < NumRows; ++I) {
308 SmallVector<int, 4> Mask;
309 for (uint32_t k = 0; k < NumCols; ++k)
310 Mask.push_back(k * NumRows + I);
311 Rows.push_back(Builder.buildShuffleVector(VecTy, MatrixReg, MatrixReg, Mask)
312 .getReg(0));
313 }
314 for (Register R : Rows) {
315 setRegClassType(R, SpvRowType, GR, &MRI, Builder.getMF());
316 }
317 return Rows;
318}
319
320Register SPIRVCombinerHelper::computeDotProduct(Register RowA, Register ColB,
321 SPIRVTypeInst SpvVecType,
322 SPIRVGlobalRegistry *GR) const {
323 SPIRVTypeInst SpvScalarType = GR->getScalarOrVectorComponentType(SpvVecType);
324 bool IsFloatOp = SpvScalarType->getOpcode() == SPIRV::OpTypeFloat;
325 LLT VecTy = GR->getRegType(SpvVecType);
326
327 Register DotRes;
328 if (isVectorType(SpvVecType)) {
329 LLT ScalarTy = VecTy.getElementType();
330 Intrinsic::SPVIntrinsics DotIntrinsic =
331 (IsFloatOp ? Intrinsic::spv_fdot : Intrinsic::spv_udot);
332 DotRes = Builder.buildIntrinsic(DotIntrinsic, {ScalarTy})
333 .addUse(RowA)
334 .addUse(ColB)
335 .getReg(0);
336 } else {
337 if (IsFloatOp)
338 DotRes = Builder.buildFMul(VecTy, RowA, ColB).getReg(0);
339 else
340 DotRes = Builder.buildMul(VecTy, RowA, ColB).getReg(0);
341 }
342 setRegClassType(DotRes, SpvScalarType, GR, &MRI, Builder.getMF());
343 return DotRes;
344}
345
346SmallVector<Register, 16> SPIRVCombinerHelper::computeDotProducts(
348 SPIRVTypeInst SpvVecType, SPIRVGlobalRegistry *GR) const {
349 SmallVector<Register, 16> ResultScalars;
350 for (uint32_t J = 0; J < ColsB.size(); ++J) {
351 for (uint32_t I = 0; I < RowsA.size(); ++I) {
352 ResultScalars.push_back(
353 computeDotProduct(RowsA[I], ColsB[J], SpvVecType, GR));
354 }
355 }
356 return ResultScalars;
357}
358
360SPIRVCombinerHelper::getDotProductVectorType(Register ResReg, uint32_t K,
361 SPIRVGlobalRegistry *GR) const {
362 // Loop over all non debug uses of ResReg
363 Type *ScalarResType = nullptr;
364 for (auto &UseMI : MRI.use_instructions(ResReg)) {
365 if (UseMI.getOpcode() != TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS)
366 continue;
367
368 if (!isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type))
369 continue;
370
371 Type *Ty = getMDOperandAsType(UseMI.getOperand(2).getMetadata(), 0);
372 if (Ty->isVectorTy())
373 ScalarResType = cast<VectorType>(Ty)->getElementType();
374 else
375 ScalarResType = Ty;
376 assert(ScalarResType->isIntegerTy() || ScalarResType->isFloatingPointTy());
377 break;
378 }
379 if (!ScalarResType)
380 llvm_unreachable("Could not determine scalar result type");
381 Type *VecType =
382 (K > 1 ? FixedVectorType::get(ScalarResType, K) : ScalarResType);
383 return GR->getOrCreateSPIRVType(VecType, Builder,
384 SPIRV::AccessQualifier::None, false);
385}
386
388 Register ResReg = MI.getOperand(0).getReg();
389 Register AReg = MI.getOperand(2).getReg();
390 Register BReg = MI.getOperand(3).getReg();
391 uint32_t NumRowsA = MI.getOperand(4).getImm();
392 uint32_t NumColsA = MI.getOperand(5).getImm();
393 uint32_t NumColsB = MI.getOperand(6).getImm();
394
395 Builder.setInstrAndDebugLoc(MI);
396
398 MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();
399
400 SPIRVTypeInst SpvVecType = getDotProductVectorType(ResReg, NumColsA, GR);
402 extractColumns(BReg, NumColsB, SpvVecType, GR);
404 extractRows(AReg, NumRowsA, NumColsA, SpvVecType, GR);
405 SmallVector<Register, 16> ResultScalars =
406 computeDotProducts(RowsA, ColsB, SpvVecType, GR);
407
408 if (ResultScalars.size() == 1)
409 Builder.buildCopy(ResReg, ResultScalars[0]);
410 else
411 Builder.buildBuildVector(ResReg, ResultScalars);
412 MI.eraseFromParent();
413}
MachineInstrBuilder & UseMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
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
static std::pair< Value *, APInt > getMask(Value *WideMask, unsigned Factor, ElementCount LeafValueEC)
#define I(x, y, z)
Definition MD5.cpp:57
Contains matchers for matching SSA Machine Instructions.
Promote Memory to Register
Definition Mem2Reg.cpp:110
void changeSign()
Definition APFloat.h:1393
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ FCMP_OLT
0 1 0 0 True if ordered and less than
Definition InstrTypes.h:746
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
Definition InstrTypes.h:744
@ FCMP_ULT
1 1 0 0 True if unordered or less than
Definition InstrTypes.h:754
@ FCMP_UGT
1 0 1 0 True if unordered or greater than
Definition InstrTypes.h:752
MachineRegisterInfo & MRI
const LegalizerInfo * LI
MachineDominatorTree * MDT
GISelValueTracking * VT
GISelChangeObserver & Observer
MachineIRBuilder & Builder
ConstantFP - Floating Point Values [float, double].
Definition Constants.h:420
bool isZero() const
Return true if the value is positive or negative zero.
Definition Constants.h:467
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
Definition Type.cpp:867
Represents a G_BUILD_VECTOR.
Abstract class that contains various methods for clients to notify about changes.
LLT getElementType() const
Returns the vector's element type. Only valid for vector types.
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.
const MachineOperand & getOperand(unsigned i) const
Register getReg() const
getReg - Returns the register number.
const MachineFunction & getMF() const
LLVM_ABI Register createGenericVirtualRegister(LLT Ty, StringRef Name="")
Create and return a new generic virtual register with low-level type Ty.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
void applyMatrixMultiply(MachineInstr &MI) const
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,...
void applyMatrixTranspose(MachineInstr &MI) const
bool matchFDivToNormalize(MachineInstr &MI) const
This match is part of a combine that rewrites X / length(X) to normalize(X) (vXf32 (g_fdiv (vXf32 X) ...
LLVM_ABI 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)
void applySPIRVNormalize(MachineInstr &MI) const
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...
LLT getRegType(SPIRVTypeInst SpvType) const
SPIRVTypeInst getScalarOrVectorComponentType(SPIRVTypeInst Type) const
SPIRVTypeInst getOrCreateSPIRVType(const Type *Type, MachineInstr &I, SPIRV::AccessQualifier::AccessQualifier AQ, bool EmitIR)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool isVectorTy() const
True if this is an instance of VectorType.
Definition Type.h:288
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:257
#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()
GInstrBind< GBuildVector > m_GBuildVector(GBuildVector *&Inst)
operand_type_match m_Pred()
SpecificConstantMatch m_ZeroInt()
Convenience matchers for specific integer values.
BinaryOp_match< LHS, RHS, TargetOpcode::G_FSUB, false > m_GFSub(const LHS &L, const RHS &R)
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)
GInstrBind< GIntrinsic > m_GIntrinsic(GIntrinsic *&Inst)
Binds the defining instruction of Reg if it is a GIntrinsic (any of the four G_INTRINSIC* opcodes).
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.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1739
bool isVectorType(SPIRVTypeInst SPVTy)
void setRegClassType(Register Reg, SPIRVTypeInst SpvType, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI, const MachineFunction &MF, bool Force)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
Type * getMDOperandAsType(const MDNode *N, unsigned I)
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID)
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:880