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"
17
18using namespace llvm;
19using namespace MIPatternMatch;
20
26
27/// This match is part of a combine that
28/// rewrites length(X - Y) to distance(X, Y)
29/// (f32 (g_intrinsic length
30/// (g_fsub (vXf32 X) (vXf32 Y))))
31/// ->
32/// (f32 (g_intrinsic distance
33/// (vXf32 X) (vXf32 Y)))
34///
37 return false;
38
39 // First operand of MI is `G_INTRINSIC` so start at operand 2.
40 Register SubReg = MI.getOperand(2).getReg();
41 return mi_match(SubReg, MRI, m_GFSub(m_Reg(), m_Reg()));
42}
43
45 // Extract the operands for X and Y from the match criteria.
46 Register SubDestReg = MI.getOperand(2).getReg();
47 MachineInstr *SubInstr = MRI.getVRegDef(SubDestReg);
48 Register SubOperand1 = SubInstr->getOperand(1).getReg();
49 Register SubOperand2 = SubInstr->getOperand(2).getReg();
50 Register ResultReg = MI.getOperand(0).getReg();
51
52 Builder.setInstrAndDebugLoc(MI);
53 Builder.buildIntrinsic(Intrinsic::spv_distance, ResultReg)
54 .addUse(SubOperand1)
55 .addUse(SubOperand2);
56
57 MI.eraseFromParent();
58}
59
60/// This match is part of a combine that
61/// rewrites X / length(X) to normalize(X)
62/// (vXf32 (g_fdiv
63/// (vXf32 X)
64/// (vXf32 splat
65/// (f32 (g_intrinsic length (vXf32 X))))))
66/// ->
67/// (vXf32 (g_intrinsic normalize (vXf32 X)))
68///
70 Register NumeratorReg = MI.getOperand(1).getReg();
71 Register DivisorReg = MI.getOperand(2).getReg();
72
73 // Match the divisor as a splat of length, inserted into lane 0.
74 MachineInstr *ShuffleInstr = MRI.getVRegDef(DivisorReg);
75 if (ShuffleInstr->getOpcode() != TargetOpcode::G_SHUFFLE_VECTOR)
76 return false;
77 if (!all_of(cast<GShuffleVector>(ShuffleInstr)->getMask(),
78 [](int M) { return M == 0; }))
79 return false;
80
81 MachineInstr *InsertInstr =
82 MRI.getVRegDef(ShuffleInstr->getOperand(1).getReg());
83 if (!isSpvIntrinsic(*InsertInstr, Intrinsic::spv_insertelt))
84 return false;
85 if (!mi_match(InsertInstr->getOperand(4).getReg(), MRI, m_ZeroInt()))
86 return false;
87
88 MachineInstr *LengthInstr =
89 MRI.getVRegDef(InsertInstr->getOperand(3).getReg());
90 if (!isSpvIntrinsic(*LengthInstr, Intrinsic::spv_length))
91 return false;
92
93 // Check that length's argument is the same as the numerator.
94 return LengthInstr->getOperand(2).getReg() == NumeratorReg;
95}
96
98 // Extract the operand for X from the match criteria.
99 Register NumeratorReg = MI.getOperand(1).getReg();
100 Register ResultReg = MI.getOperand(0).getReg();
101
102 Builder.setInstrAndDebugLoc(MI);
103 Builder.buildIntrinsic(Intrinsic::spv_normalize, ResultReg)
104 .addUse(NumeratorReg);
105
106 MI.eraseFromParent();
107}
108
109/// This match is part of a combine that
110/// rewrites select(fcmp(dot(I, Ng), 0), N, -N) to faceforward(N, I, Ng)
111/// (vXf32 (g_select
112/// (g_fcmp
113/// (g_intrinsic dot(vXf32 I) (vXf32 Ng)
114/// 0)
115/// (vXf32 N)
116/// (vXf32 g_fneg (vXf32 N))))
117/// ->
118/// (vXf32 (g_intrinsic faceforward
119/// (vXf32 N) (vXf32 I) (vXf32 Ng)))
120///
121/// This only works for Vulkan shader targets.
122///
124 if (!STI.isShader())
125 return false;
126
127 // Match overall select pattern.
128 Register CondReg, TrueReg, FalseReg;
129 if (!mi_match(MI.getOperand(0).getReg(), MRI,
130 m_GISelect(m_Reg(CondReg), m_Reg(TrueReg), m_Reg(FalseReg))))
131 return false;
132
133 // Match the FCMP condition.
134 Register DotReg, CondZeroReg;
136 if (!mi_match(CondReg, MRI,
137 m_GFCmp(m_Pred(Pred), m_Reg(DotReg), m_Reg(CondZeroReg))))
138 return false;
139 if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)
140 std::swap(DotReg, CondZeroReg);
141 else if (!(Pred == CmpInst::FCMP_OLT || Pred == CmpInst::FCMP_ULT))
142 return false;
143
144 // Check if FCMP is a comparison between a dot product and 0.
146 Register DotOperand1, DotOperand2;
147 // Check for scalar dot product.
148 if (!mi_match(DotReg, MRI,
149 m_GFMul(m_Reg(DotOperand1), m_Reg(DotOperand2))) ||
150 !MRI.getType(DotOperand1).isScalar() ||
151 !MRI.getType(DotOperand2).isScalar())
152 return false;
153 }
154
155 const ConstantFP *ZeroVal;
156 if (!mi_match(CondZeroReg, MRI, m_GFCst(ZeroVal)) || !ZeroVal->isZero())
157 return false;
158
159 // Check if select's false operand is the negation of the true operand.
160 auto AreNegatedConstantsOrSplats = [&](Register TrueReg, Register FalseReg) {
161 std::optional<FPValueAndVReg> TrueVal, FalseVal;
162 if (!mi_match(TrueReg, MRI, m_GFCstOrSplat(TrueVal)) ||
163 !mi_match(FalseReg, MRI, m_GFCstOrSplat(FalseVal)))
164 return false;
165 APFloat TrueValNegated = TrueVal->Value;
166 TrueValNegated.changeSign();
167 return FalseVal->Value.compare(TrueValNegated) == APFloat::cmpEqual;
168 };
169
170 if (!mi_match(TrueReg, MRI, m_GFNeg(m_SpecificReg(FalseReg))) &&
171 !mi_match(FalseReg, MRI, m_GFNeg(m_SpecificReg(TrueReg)))) {
172 std::optional<FPValueAndVReg> MulConstant;
173 GBuildVector *TrueInstr, *FalseInstr;
174 if (mi_match(TrueReg, MRI, m_GBuildVector(TrueInstr)) &&
175 mi_match(FalseReg, MRI, m_GBuildVector(FalseInstr)) &&
176 TrueInstr->getNumOperands() == FalseInstr->getNumOperands()) {
177 for (unsigned I = 1; I < TrueInstr->getNumOperands(); ++I)
178 if (!AreNegatedConstantsOrSplats(TrueInstr->getOperand(I).getReg(),
179 FalseInstr->getOperand(I).getReg()))
180 return false;
181 } else if (mi_match(TrueReg, MRI,
182 m_GFMul(m_SpecificReg(FalseReg),
183 m_GFCstOrSplat(MulConstant))) ||
184 mi_match(FalseReg, MRI,
185 m_GFMul(m_SpecificReg(TrueReg),
186 m_GFCstOrSplat(MulConstant))) ||
187 mi_match(TrueReg, MRI,
188 m_GFMul(m_GFCstOrSplat(MulConstant),
189 m_SpecificReg(FalseReg))) ||
190 mi_match(FalseReg, MRI,
191 m_GFMul(m_GFCstOrSplat(MulConstant),
192 m_SpecificReg(TrueReg)))) {
193 if (!MulConstant || !MulConstant->Value.isMinusOne())
194 return false;
195 } else if (!AreNegatedConstantsOrSplats(TrueReg, FalseReg))
196 return false;
197 }
198
199 return true;
200}
201
203 // Extract the operands for N, I, and Ng from the match criteria.
204 Register CondReg = MI.getOperand(1).getReg();
205 MachineInstr *CondInstr = MRI.getVRegDef(CondReg);
206 Register DotReg = CondInstr->getOperand(2).getReg();
207 CmpInst::Predicate Pred = cast<GFCmp>(CondInstr)->getCond();
208 if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)
209 DotReg = CondInstr->getOperand(3).getReg();
210 MachineInstr *DotInstr = MRI.getVRegDef(DotReg);
211 Register DotOperand1, DotOperand2;
212 if (DotInstr->getOpcode() == TargetOpcode::G_FMUL) {
213 DotOperand1 = DotInstr->getOperand(1).getReg();
214 DotOperand2 = DotInstr->getOperand(2).getReg();
215 } else {
216 DotOperand1 = DotInstr->getOperand(2).getReg();
217 DotOperand2 = DotInstr->getOperand(3).getReg();
218 }
219 Register TrueReg = MI.getOperand(2).getReg();
220 Register FalseReg = MI.getOperand(3).getReg();
221 MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
222 if (TrueInstr->getOpcode() == TargetOpcode::G_FNEG ||
223 TrueInstr->getOpcode() == TargetOpcode::G_FMUL)
224 std::swap(TrueReg, FalseReg);
225
226 Register ResultReg = MI.getOperand(0).getReg();
227 Builder.setInstrAndDebugLoc(MI);
228 Builder.buildIntrinsic(Intrinsic::spv_faceforward, ResultReg)
229 .addUse(TrueReg) // N
230 .addUse(DotOperand1) // I
231 .addUse(DotOperand2); // Ng
232
233 MI.eraseFromParent();
234}
235
237 Register ResReg = MI.getOperand(0).getReg();
238 Register InReg = MI.getOperand(2).getReg();
239 uint32_t Rows = MI.getOperand(3).getImm();
240 uint32_t Cols = MI.getOperand(4).getImm();
241
242 Builder.setInstrAndDebugLoc(MI);
243
244 // A 1xN or Nx1 transpose is a pure reshape.
245 if (Rows == 1 || Cols == 1) {
246 Builder.buildCopy(ResReg, InReg);
247 MI.eraseFromParent();
248 return;
249 }
250
252 for (uint32_t K = 0; K < Rows * Cols; ++K) {
253 uint32_t R = K / Cols;
254 uint32_t C = K % Cols;
255 Mask.push_back(C * Rows + R);
256 }
257
258 Builder.buildShuffleVector(ResReg, InReg, InReg, Mask);
259 MI.eraseFromParent();
260}
261
263SPIRVCombinerHelper::extractColumns(Register MatrixReg, uint32_t NumberOfCols,
264 SPIRVTypeInst SpvColType,
265 SPIRVGlobalRegistry *GR) const {
266 // If the matrix is a single colunm, return that single column.
267 if (NumberOfCols == 1)
268 return {MatrixReg};
269
271 LLT ColTy = GR->getRegType(SpvColType);
272 for (uint32_t J = 0; J < NumberOfCols; ++J)
274 Builder.buildUnmerge(Cols, MatrixReg);
275 for (Register R : Cols) {
276 setRegClassType(R, SpvColType, GR, &MRI, Builder.getMF());
277 }
278 return Cols;
279}
280
282SPIRVCombinerHelper::extractRows(Register MatrixReg, uint32_t NumRows,
283 uint32_t NumCols, SPIRVTypeInst SpvRowType,
284 SPIRVGlobalRegistry *GR) const {
286 LLT VecTy = GR->getRegType(SpvRowType);
287
288 // If there is only one column, then each row is a scalar that needs
289 // to be extracted.
290 if (NumCols == 1) {
291 assert(!isVectorType(SpvRowType));
292 for (uint32_t I = 0; I < NumRows; ++I)
293 Rows.push_back(MRI.createGenericVirtualRegister(VecTy));
294 Builder.buildUnmerge(Rows, MatrixReg);
295 for (Register R : Rows) {
296 setRegClassType(R, SpvRowType, GR, &MRI, Builder.getMF());
297 }
298 return Rows;
299 }
300
301 // If the matrix is a single row return that row.
302 if (NumRows == 1) {
303 return {MatrixReg};
304 }
305
306 for (uint32_t I = 0; I < NumRows; ++I) {
307 SmallVector<int, 4> Mask;
308 for (uint32_t k = 0; k < NumCols; ++k)
309 Mask.push_back(k * NumRows + I);
310 Rows.push_back(Builder.buildShuffleVector(VecTy, MatrixReg, MatrixReg, Mask)
311 .getReg(0));
312 }
313 for (Register R : Rows) {
314 setRegClassType(R, SpvRowType, GR, &MRI, Builder.getMF());
315 }
316 return Rows;
317}
318
319Register SPIRVCombinerHelper::computeDotProduct(Register RowA, Register ColB,
320 SPIRVTypeInst SpvVecType,
321 SPIRVGlobalRegistry *GR) const {
322 SPIRVTypeInst SpvScalarType = GR->getScalarOrVectorComponentType(SpvVecType);
323 bool IsFloatOp = SpvScalarType->getOpcode() == SPIRV::OpTypeFloat;
324 LLT VecTy = GR->getRegType(SpvVecType);
325
326 Register DotRes;
327 if (isVectorType(SpvVecType)) {
328 LLT ScalarTy = VecTy.getElementType();
329 Intrinsic::SPVIntrinsics DotIntrinsic =
330 (IsFloatOp ? Intrinsic::spv_fdot : Intrinsic::spv_udot);
331 DotRes = Builder.buildIntrinsic(DotIntrinsic, {ScalarTy})
332 .addUse(RowA)
333 .addUse(ColB)
334 .getReg(0);
335 } else {
336 if (IsFloatOp)
337 DotRes = Builder.buildFMul(VecTy, RowA, ColB).getReg(0);
338 else
339 DotRes = Builder.buildMul(VecTy, RowA, ColB).getReg(0);
340 }
341 setRegClassType(DotRes, SpvScalarType, GR, &MRI, Builder.getMF());
342 return DotRes;
343}
344
345SmallVector<Register, 16> SPIRVCombinerHelper::computeDotProducts(
347 SPIRVTypeInst SpvVecType, SPIRVGlobalRegistry *GR) const {
348 SmallVector<Register, 16> ResultScalars;
349 for (uint32_t J = 0; J < ColsB.size(); ++J) {
350 for (uint32_t I = 0; I < RowsA.size(); ++I) {
351 ResultScalars.push_back(
352 computeDotProduct(RowsA[I], ColsB[J], SpvVecType, GR));
353 }
354 }
355 return ResultScalars;
356}
357
359SPIRVCombinerHelper::getDotProductVectorType(Register ResReg, uint32_t K,
360 SPIRVGlobalRegistry *GR) const {
361 // Loop over all non debug uses of ResReg
362 Type *ScalarResType = nullptr;
363 for (auto &UseMI : MRI.use_instructions(ResReg)) {
364 if (UseMI.getOpcode() != TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS)
365 continue;
366
367 if (!isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type))
368 continue;
369
370 Type *Ty = getMDOperandAsType(UseMI.getOperand(2).getMetadata(), 0);
371 if (Ty->isVectorTy())
372 ScalarResType = cast<VectorType>(Ty)->getElementType();
373 else
374 ScalarResType = Ty;
375 assert(ScalarResType->isIntegerTy() || ScalarResType->isFloatingPointTy());
376 break;
377 }
378 if (!ScalarResType)
379 llvm_unreachable("Could not determine scalar result type");
380 Type *VecType =
381 (K > 1 ? FixedVectorType::get(ScalarResType, K) : ScalarResType);
382 return GR->getOrCreateSPIRVType(VecType, Builder,
383 SPIRV::AccessQualifier::None, false);
384}
385
387 Register ResReg = MI.getOperand(0).getReg();
388 Register AReg = MI.getOperand(2).getReg();
389 Register BReg = MI.getOperand(3).getReg();
390 uint32_t NumRowsA = MI.getOperand(4).getImm();
391 uint32_t NumColsA = MI.getOperand(5).getImm();
392 uint32_t NumColsB = MI.getOperand(6).getImm();
393
394 Builder.setInstrAndDebugLoc(MI);
395
397 MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();
398
399 SPIRVTypeInst SpvVecType = getDotProductVectorType(ResReg, NumColsA, GR);
401 extractColumns(BReg, NumColsB, SpvVecType, GR);
403 extractRows(AReg, NumRowsA, NumColsA, SpvVecType, GR);
404 SmallVector<Register, 16> ResultScalars =
405 computeDotProducts(RowsA, ColsB, SpvVecType, GR);
406
407 if (ResultScalars.size() == 1)
408 Builder.buildCopy(ResReg, ResultScalars[0]);
409 else
410 Builder.buildBuildVector(ResReg, ResultScalars);
411 MI.eraseFromParent();
412}
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:1401
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