LLVM 24.0.0git
SPIRVISelLowering.cpp
Go to the documentation of this file.
1//===- SPIRVISelLowering.cpp - SPIR-V DAG Lowering Impl ---------*- C++ -*-===//
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 file implements the SPIRVTargetLowering class.
10//
11//===----------------------------------------------------------------------===//
12
13#include "SPIRVISelLowering.h"
14#include "SPIRV.h"
15#include "SPIRVInstrInfo.h"
17#include "SPIRVRegisterInfo.h"
18#include "SPIRVSubtarget.h"
23#include "llvm/IR/IntrinsicsSPIRV.h"
24
25#define DEBUG_TYPE "spirv-lower"
26
27using namespace llvm;
28
30 const SPIRVSubtarget &ST)
31 : TargetLowering(TM, ST), STI(ST) {
32 // Even with SPV_ALTERA_arbitrary_precision_integers enabled, atomic sizes are
33 // limited by atomicrmw xchg operation, which only supports operand up to 64
34 // bits wide, as defined in SPIR-V legalizer. Currently, spirv-val doesn't
35 // consider 128-bit OpTypeInt as valid either.
38}
39
40// Returns true of the types logically match, as defined in
41// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpCopyLogical.
42static bool typesLogicallyMatch(const SPIRVTypeInst Ty1,
43 const SPIRVTypeInst Ty2,
45 if (Ty1->getOpcode() != Ty2->getOpcode())
46 return false;
47
48 if (Ty1->getNumOperands() != Ty2->getNumOperands())
49 return false;
50
51 if (Ty1->getOpcode() == SPIRV::OpTypeArray) {
52 // Array must have the same size.
53 if (Ty1->getOperand(2).getReg() != Ty2->getOperand(2).getReg())
54 return false;
55
56 SPIRVTypeInst ElemType1 =
58 SPIRVTypeInst ElemType2 =
60 return ElemType1 == ElemType2 ||
61 typesLogicallyMatch(ElemType1, ElemType2, GR);
62 }
63
64 if (Ty1->getOpcode() == SPIRV::OpTypeStruct) {
65 for (unsigned I = 1; I < Ty1->getNumOperands(); I++) {
66 SPIRVTypeInst ElemType1 =
68 SPIRVTypeInst ElemType2 =
70 if (ElemType1 != ElemType2 &&
71 !typesLogicallyMatch(ElemType1, ElemType2, GR))
72 return false;
73 }
74 return true;
75 }
76 return false;
77}
78
80 LLVMContext &Context, CallingConv::ID CC, EVT VT) const {
81 // This code avoids CallLowering fail inside getVectorTypeBreakdown
82 // on v3i1 arguments. Maybe we need to return 1 for all types.
83 // TODO: remove it once this case is supported by the default implementation.
84 if (VT.isVector() && VT.getVectorNumElements() == 3 &&
85 (VT.getVectorElementType() == MVT::i1 ||
86 VT.getVectorElementType() == MVT::i8))
87 return 1;
88 if (!VT.isVector() && VT.isInteger() && VT.getSizeInBits() <= 64)
89 return 1;
90 return getNumRegisters(Context, VT);
91}
92
95 EVT VT) const {
96 // This code avoids CallLowering fail inside getVectorTypeBreakdown
97 // on v3i1 arguments. Maybe we need to return i32 for all types.
98 // TODO: remove it once this case is supported by the default implementation.
99 if (VT.isVector()) {
100 if (VT.getVectorNumElements() == 3) {
101 if (VT.getVectorElementType() == MVT::i1)
102 return MVT::v4i1;
103 else if (VT.getVectorElementType() == MVT::i8)
104 return MVT::v4i8;
105 } else if (!isPowerOf2_32(VT.getVectorNumElements()) &&
106 STI.canUseExtension(SPIRV::Extension::SPV_EXT_long_vector)) {
107 // Non POT element counts are not yet supported by GISEL.
108 return MVT::getVectorVT(
111 }
112 }
113 return getRegisterType(Context, VT);
114}
115
118 MachineFunction &MF, unsigned Intrinsic) const {
119 IntrinsicInfo Info;
120
121 unsigned AlignIdx = 0;
122 unsigned OrderingIdx = 0;
123 unsigned FlagsIdx;
124
125 switch (Intrinsic) {
126 case Intrinsic::spv_load:
127 FlagsIdx = 1;
128 AlignIdx = 2;
129 break;
130 case Intrinsic::spv_store:
131 FlagsIdx = 2;
132 AlignIdx = 3;
133 break;
134 case Intrinsic::spv_atomic_load:
135 FlagsIdx = 1;
136 OrderingIdx = 2;
137 break;
138 case Intrinsic::spv_atomic_store:
139 FlagsIdx = 2;
140 OrderingIdx = 3;
141 break;
142 default:
143 return;
144 }
145
146 Info.flags = static_cast<MachineMemOperand::Flags>(
147 cast<ConstantInt>(I.getOperand(FlagsIdx))->getZExtValue());
148 Info.memVT = MVT::i64;
149 // TODO: take into account opaque pointers (don't use getElementType).
150 // MVT::getVT(PtrTy->getElementType());
151
152 if (AlignIdx) {
153 auto *AlignOp = cast<ConstantInt>(I.getOperand(AlignIdx));
154 Info.align = Align(AlignOp->getZExtValue());
155 }
156
157 if (OrderingIdx) {
158 Info.order = static_cast<AtomicOrdering>(
159 cast<ConstantInt>(I.getOperand(OrderingIdx))->getZExtValue());
160 }
161 Infos.push_back(Info);
162}
163
166 // SPIR-V represents inline assembly via OpAsmINTEL where constraints are
167 // passed through as literals defined by client API. Return C_RegisterClass
168 // for non-memory constraints since SPIR-V does not distinguish between
169 // register, immediate, or memory operands at this level. We do have to return
170 // C_Memory for memory constraints as otherwise IRTranslator gets confused
171 // trying to allocate registers for them.
172 if (Constraint == "m")
173 return C_Memory;
174 return C_RegisterClass;
175}
176
177std::pair<unsigned, const TargetRegisterClass *>
179 StringRef Constraint,
180 MVT VT) const {
181 const TargetRegisterClass *RC = nullptr;
182 if (Constraint.starts_with("{"))
183 return std::make_pair(0u, RC);
184
185 if (VT.isFloatingPoint())
186 RC = VT.isVector() ? &SPIRV::vfIDRegClass : &SPIRV::fIDRegClass;
187 else if (VT.isInteger())
188 RC = VT.isVector() ? &SPIRV::viIDRegClass : &SPIRV::iIDRegClass;
189 else
190 RC = &SPIRV::iIDRegClass;
191
192 return std::make_pair(0u, RC);
193}
194
196 const MachineInstr *Inst = MRI->getVRegDef(OpReg);
197 return Inst && Inst->getOpcode() == SPIRV::OpFunctionParameter
198 ? Inst->getOperand(1).getReg()
199 : OpReg;
200}
201
204 Register OpReg, unsigned OpIdx,
205 SPIRVTypeInst NewPtrType) {
206 MachineIRBuilder MIB(I);
207 Register NewReg = createVirtualRegister(NewPtrType, &GR, MRI, MIB.getMF());
208 MIB.buildInstr(SPIRV::OpBitcast)
209 .addDef(NewReg)
210 .addUse(GR.getSPIRVTypeID(NewPtrType))
211 .addUse(OpReg)
213 *STI.getRegBankInfo());
214 I.getOperand(OpIdx).setReg(NewReg);
215}
216
218 SPIRVTypeInst OpType, bool ReuseType,
219 SPIRVTypeInst ResType,
220 const Type *ResTy) {
221 SPIRV::StorageClass::StorageClass SC =
222 static_cast<SPIRV::StorageClass::StorageClass>(
223 OpType->getOperand(1).getImm());
224 MachineIRBuilder MIB(I);
225 SPIRVTypeInst NewBaseType =
226 ReuseType ? ResType
228 ResTy, MIB, SPIRV::AccessQualifier::ReadWrite, false);
229 return GR.getOrCreateSPIRVPointerType(NewBaseType, MIB, SC);
230}
231
232// Insert a bitcast before the instruction to keep SPIR-V code valid
233// when there is a type mismatch between results and operand types.
234static void validatePtrTypes(const SPIRVSubtarget &STI,
236 MachineInstr &I, unsigned OpIdx,
237 SPIRVTypeInst ResType,
238 const Type *ResTy = nullptr) {
239 // Get operand type
240 MachineFunction *MF = I.getParent()->getParent();
241 Register OpReg = I.getOperand(OpIdx).getReg();
242 Register OpTypeReg = getTypeReg(MRI, OpReg);
243 const MachineInstr *OpType = GR.getSPIRVTypeForVReg(OpTypeReg, MF);
244 if (!ResType || !OpType || OpType->getOpcode() != SPIRV::OpTypePointer)
245 return;
246 // Get operand's pointee type
247 Register ElemTypeReg = OpType->getOperand(2).getReg();
248 SPIRVTypeInst ElemType = GR.getSPIRVTypeForVReg(ElemTypeReg, MF);
249 if (!ElemType)
250 return;
251 // Check if we need a bitcast to make a statement valid
252 bool IsSameMF = MF == ResType->getParent()->getParent();
253 bool IsEqualTypes = IsSameMF ? ElemType == ResType
254 : GR.getTypeForSPIRVType(ElemType) == ResTy;
255 if (IsEqualTypes)
256 return;
257 // There is a type mismatch between results and operand types
258 // and we insert a bitcast before the instruction to keep SPIR-V code valid
259 SPIRVTypeInst NewPtrType =
260 createNewPtrType(GR, I, OpType, IsSameMF, ResType, ResTy);
261 if (!GR.isBitcastCompatible(NewPtrType, OpType))
263 "insert validation bitcast: incompatible result and operand types");
264 doInsertBitcast(STI, MRI, GR, I, OpReg, OpIdx, NewPtrType);
265}
266
267// Insert a bitcast before OpGroupWaitEvents if the last argument is a pointer
268// that doesn't point to OpTypeEvent.
272 MachineInstr &I) {
273 constexpr unsigned OpIdx = 2;
274 MachineFunction *MF = I.getParent()->getParent();
275 Register OpReg = I.getOperand(OpIdx).getReg();
276 Register OpTypeReg = getTypeReg(MRI, OpReg);
277 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(OpTypeReg, MF);
278 if (!OpType || OpType->getOpcode() != SPIRV::OpTypePointer)
279 return;
280 SPIRVTypeInst ElemType =
281 GR.getSPIRVTypeForVReg(OpType->getOperand(2).getReg());
282 if (!ElemType || ElemType->getOpcode() == SPIRV::OpTypeEvent)
283 return;
284 // Insert a bitcast before the instruction to keep SPIR-V code valid.
285 LLVMContext &Context = MF->getFunction().getContext();
286 SPIRVTypeInst NewPtrType =
287 createNewPtrType(GR, I, OpType, false, nullptr,
288 TargetExtType::get(Context, "spirv.Event"));
289 doInsertBitcast(STI, MRI, GR, I, OpReg, OpIdx, NewPtrType);
290}
291
295 Register PtrReg = I.getOperand(0).getReg();
296 MachineFunction *MF = I.getParent()->getParent();
297 Register PtrTypeReg = getTypeReg(MRI, PtrReg);
298 SPIRVTypeInst PtrType = GR.getSPIRVTypeForVReg(PtrTypeReg, MF);
299 SPIRVTypeInst PonteeElemType = PtrType ? GR.getPointeeType(PtrType) : nullptr;
300 if (!PonteeElemType || PonteeElemType->getOpcode() == SPIRV::OpTypeVoid ||
301 (PonteeElemType->getOpcode() == SPIRV::OpTypeInt &&
302 PonteeElemType->getOperand(1).getImm() == 8))
303 return;
304 // To keep the code valid a bitcast must be inserted
305 SPIRV::StorageClass::StorageClass SC =
306 static_cast<SPIRV::StorageClass::StorageClass>(
307 PtrType->getOperand(1).getImm());
308 MachineIRBuilder MIB(I);
309 LLVMContext &Context = MF->getFunction().getContext();
310 SPIRVTypeInst NewPtrType =
312 doInsertBitcast(STI, MRI, GR, I, PtrReg, 0, NewPtrType);
313}
314
318 MachineInstr &I, unsigned OpIdx) {
319 MachineFunction *MF = I.getParent()->getParent();
320 Register OpReg = I.getOperand(OpIdx).getReg();
321 Register OpTypeReg = getTypeReg(MRI, OpReg);
322 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(OpTypeReg, MF);
323 if (!OpType || OpType->getOpcode() != SPIRV::OpTypePointer)
324 return;
325 SPIRVTypeInst ElemType =
326 GR.getSPIRVTypeForVReg(OpType->getOperand(2).getReg());
327 if (!ElemType || ElemType->getOpcode() != SPIRV::OpTypeStruct ||
328 ElemType->getNumOperands() != 2)
329 return;
330 // It's a structure-wrapper around another type with a single member field.
331 SPIRVTypeInst MemberType =
332 GR.getSPIRVTypeForVReg(ElemType->getOperand(1).getReg());
333 if (!MemberType)
334 return;
335 unsigned MemberTypeOp = MemberType->getOpcode();
336 if (!isVectorType(MemberType) && MemberTypeOp != SPIRV::OpTypeInt &&
337 MemberTypeOp != SPIRV::OpTypeFloat && MemberTypeOp != SPIRV::OpTypeBool)
338 return;
339 // It's a structure-wrapper around a valid type. Insert a bitcast before the
340 // instruction to keep SPIR-V code valid.
341 SPIRV::StorageClass::StorageClass SC =
342 static_cast<SPIRV::StorageClass::StorageClass>(
343 OpType->getOperand(1).getImm());
344 MachineIRBuilder MIB(I);
345 SPIRVTypeInst NewPtrType =
346 GR.getOrCreateSPIRVPointerType(MemberType, MIB, SC);
347 doInsertBitcast(STI, MRI, GR, I, OpReg, OpIdx, NewPtrType);
348}
349
350// Insert a bitcast before the function call instruction to keep SPIR-V code
351// valid when there is a type mismatch between actual and expected types of an
352// argument:
353// %formal = OpFunctionParameter %formal_type
354// ...
355// %res = OpFunctionCall %ty %fun %actual ...
356// implies that %actual is of %formal_type, and in case of opaque pointers.
357// We may need to insert a bitcast to ensure this.
359 MachineRegisterInfo *DefMRI,
360 MachineRegisterInfo *CallMRI,
361 SPIRVGlobalRegistry &GR, MachineInstr &FunCall,
362 MachineInstr *FunDef) {
363 if (FunDef->getOpcode() != SPIRV::OpFunction)
364 return;
365 unsigned OpIdx = 3;
366 for (FunDef = FunDef->getNextNode();
367 FunDef && FunDef->getOpcode() == SPIRV::OpFunctionParameter &&
368 OpIdx < FunCall.getNumOperands();
369 FunDef = FunDef->getNextNode(), OpIdx++) {
370 SPIRVTypeInst DefPtrType =
371 DefMRI->getVRegDef(FunDef->getOperand(1).getReg());
372 SPIRVTypeInst DefElemType =
373 DefPtrType && DefPtrType->getOpcode() == SPIRV::OpTypePointer
374 ? GR.getSPIRVTypeForVReg(DefPtrType->getOperand(2).getReg(),
375 DefPtrType->getParent()->getParent())
376 : nullptr;
377 if (DefElemType) {
378 const Type *DefElemTy = GR.getTypeForSPIRVType(DefElemType);
379 // validatePtrTypes() works in the context if the call site
380 // When we process historical records about forward calls
381 // we need to switch context to the (forward) call site and
382 // then restore it back to the current machine function.
383 MachineFunction *CurMF =
384 GR.setCurrentFunc(*FunCall.getParent()->getParent());
385 validatePtrTypes(STI, CallMRI, GR, FunCall, OpIdx, DefElemType,
386 DefElemTy);
387 GR.setCurrentFunc(*CurMF);
388 }
389 }
390}
391
392// Ensure there is no mismatch between actual and expected arg types: calls
393// with a processed definition. Return Function pointer if it's a forward
394// call (ahead of definition), and nullptr otherwise.
396 MachineRegisterInfo *CallMRI,
398 MachineInstr &FunCall) {
399 const GlobalValue *GV = FunCall.getOperand(2).getGlobal();
400 const Function *F = dyn_cast<Function>(GV);
401 MachineInstr *FunDef =
402 const_cast<MachineInstr *>(GR.getFunctionDefinition(F));
403 if (!FunDef)
404 return F;
405 MachineRegisterInfo *DefMRI = &FunDef->getParent()->getParent()->getRegInfo();
406 validateFunCallMachineDef(STI, DefMRI, CallMRI, GR, FunCall, FunDef);
407 return nullptr;
408}
409
410// Ensure there is no mismatch between actual and expected arg types: calls
411// ahead of a processed definition.
414 MachineInstr &FunDef) {
415 const Function *F = GR.getFunctionByDefinition(&FunDef);
417 for (MachineInstr *FunCall : *FwdCalls) {
418 MachineRegisterInfo *CallMRI =
419 &FunCall->getParent()->getParent()->getRegInfo();
420 validateFunCallMachineDef(STI, DefMRI, CallMRI, GR, *FunCall, &FunDef);
421 }
422}
423
424// Validation of an access chain.
427 SPIRVTypeInst BaseTypeInst = GR.getSPIRVTypeForVReg(I.getOperand(0).getReg());
428 if (BaseTypeInst && BaseTypeInst->getOpcode() == SPIRV::OpTypePointer) {
429 SPIRVTypeInst BaseElemType =
430 GR.getSPIRVTypeForVReg(BaseTypeInst->getOperand(2).getReg());
431 validatePtrTypes(STI, MRI, GR, I, 2, BaseElemType);
432 }
433}
434
437 // IRTranslator does not believe that rank-1 vectors exist, unlike upstream
438 // LLVM which happily creates <1 x T> vectors. This leads to operations over
439 // <1 x T> vectors getting translated as their scalar counterparts, which is
440 // wrong if we used SPV_EXT_long_vector to preserve the actual vector-ness.
441 switch (MI.getOpcode()) {
442 case SPIRV::OpBitwiseAndS:
443 case SPIRV::OpBitwiseOrS:
444 case SPIRV::OpBitwiseXorS:
445 case SPIRV::OpFAddS:
446 case SPIRV::OpFDivS:
447 case SPIRV::OpFMulS:
448 case SPIRV::OpFNegate:
449 case SPIRV::OpFRemS:
450 case SPIRV::OpFSubS:
451 case SPIRV::OpIAddCarryS:
452 case SPIRV::OpIAddS:
453 case SPIRV::OpIMulS:
454 case SPIRV::OpISubBorrowS:
455 case SPIRV::OpISubS:
456 case SPIRV::OpSDivS:
457 case SPIRV::OpSRemS:
458 case SPIRV::OpShiftLeftLogicalS:
459 case SPIRV::OpShiftRightArithmeticS:
460 case SPIRV::OpShiftRightLogicalS:
461 case SPIRV::OpStrictFAddS:
462 case SPIRV::OpStrictFDivS:
463 case SPIRV::OpStrictFMulS:
464 case SPIRV::OpStrictFRemS:
465 case SPIRV::OpStrictFSubS:
466 case SPIRV::OpUDivS:
467 case SPIRV::OpUModS: {
468 SPIRVTypeInst ResTy = GR.getSPIRVTypeForVReg(MI.getOperand(1).getReg());
469
470 if (!isVectorType(ResTy))
471 return;
472
473 // Restore original Vec1 type.
474 Register NewResultReg = createVirtualRegister(ResTy, &GR, MRI, *MI.getMF());
475 MRI->replaceRegWith(MI.getOperand(0).getReg(), NewResultReg);
476 // Vector opcodes are always next after scalar (if this ceases to hold we
477 // will have to adapt).
478 MI.setDesc(STI.getInstrInfo()->get(MI.getOpcode() + 1));
479 // IRTranslator would've inserted COPYs from the vector into a scalar, which
480 // are spurious and have to be walked through.
481 for (unsigned I = 2; I != MI.getNumOperands(); ++I) {
482 MachineOperand &Op = MI.getOperand(I);
483 if (!Op.isReg())
484 continue;
485
487 if (OpTy == ResTy)
488 continue;
489
490 MachineInstr *OpDef = getDef(Op, MRI);
491 assert(OpDef &&
492 GR.getSPIRVTypeForVReg(OpDef->getOperand(0).getReg()) == ResTy &&
493 "Expected to find Result Type (Vec1)!");
494 MI.substituteRegister(Op.getReg(), OpDef->getOperand(0).getReg(), 0,
495 *STI.getRegisterInfo());
496 }
497 break;
498 }
499 case TargetOpcode::COPY: {
500 Register ResVReg = MI.getOperand(0).getReg();
501 SPIRVTypeInst SrcTy = GR.getSPIRVTypeForVReg(MI.getOperand(1).getReg());
502 SPIRVTypeInst DstTy = GR.getSPIRVTypeForVReg(ResVReg);
503
504 if (!SrcTy || !DstTy || isVectorType(DstTy) || !isVectorType(SrcTy))
505 return;
506
507 Register ExtractReg = createVirtualRegister(DstTy, &GR, MRI, *MI.getMF());
508 BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
509 STI.getInstrInfo()->get(SPIRV::OpCompositeExtract))
510 .addDef(ExtractReg)
511 .addUse(GR.getSPIRVTypeID(DstTy))
512 .addUse(MI.getOperand(1).getReg())
513 .addImm(0);
514 for (auto &&U : MRI->use_instructions(ResVReg))
515 U.substituteRegister(ResVReg, ExtractReg, 0, *STI.getRegisterInfo());
516 break;
517 }
518 default:
519 break;
520 }
521}
522
523// TODO: the logic of inserting additional bitcast's is to be moved
524// to pre-IRTranslation passes eventually
526 // finalizeLowering() is called twice (see GlobalISel/InstructionSelect.cpp)
527 // We'd like to avoid the needless second processing pass.
529 return;
530
531 MachineRegisterInfo *MRI = &MF.getRegInfo();
532 SPIRVGlobalRegistry &GR = *STI.getSPIRVGlobalRegistry();
533 GR.setCurrentFunc(MF);
534 for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) {
536 for (MachineBasicBlock::iterator MBBI = MBB->begin(), MBBE = MBB->end();
537 MBBI != MBBE;) {
538 MachineInstr &MI = *MBBI++;
539 validateVec1Ops(STI, MRI, GR, MI);
540 switch (MI.getOpcode()) {
541 case SPIRV::OpAtomicLoad:
542 case SPIRV::OpAtomicExchange:
543 case SPIRV::OpAtomicCompareExchange:
544 case SPIRV::OpAtomicCompareExchangeWeak:
545 case SPIRV::OpAtomicIIncrement:
546 case SPIRV::OpAtomicIDecrement:
547 case SPIRV::OpAtomicIAdd:
548 case SPIRV::OpAtomicISub:
549 case SPIRV::OpAtomicSMin:
550 case SPIRV::OpAtomicUMin:
551 case SPIRV::OpAtomicSMax:
552 case SPIRV::OpAtomicUMax:
553 case SPIRV::OpAtomicAnd:
554 case SPIRV::OpAtomicOr:
555 case SPIRV::OpAtomicXor:
556 // for the above listed instructions
557 // OpAtomicXXX <ResType>, ptr %Op, ...
558 // implies that %Op is a pointer to <ResType>
559 case SPIRV::OpLoad:
560 // OpLoad <ResType>, ptr %Op implies that %Op is a pointer to <ResType>
562 break;
563
564 validatePtrTypes(STI, MRI, GR, MI, 2,
565 GR.getSPIRVTypeForVReg(MI.getOperand(0).getReg()));
566 break;
567 case SPIRV::OpAtomicStore:
568 // OpAtomicStore ptr %Op, <Scope>, <Mem>, <Obj>
569 // implies that %Op points to the <Obj>'s type
570 validatePtrTypes(STI, MRI, GR, MI, 0,
571 GR.getSPIRVTypeForVReg(MI.getOperand(3).getReg()));
572 break;
573 case SPIRV::OpStore:
574 // OpStore ptr %Op, <Obj> implies that %Op points to the <Obj>'s type
575 validatePtrTypes(STI, MRI, GR, MI, 0,
576 GR.getSPIRVTypeForVReg(MI.getOperand(1).getReg()));
577 break;
578 case SPIRV::OpPtrCastToGeneric:
579 case SPIRV::OpGenericCastToPtr:
580 case SPIRV::OpGenericCastToPtrExplicit:
581 validateAccessChain(STI, MRI, GR, MI);
582 break;
583 case SPIRV::OpPtrAccessChain:
584 case SPIRV::OpInBoundsPtrAccessChain:
585 if (MI.getNumOperands() == 4)
586 validateAccessChain(STI, MRI, GR, MI);
587 break;
588
589 case SPIRV::OpFunctionCall:
590 // ensure there is no mismatch between actual and expected arg types:
591 // calls with a processed definition
592 if (MI.getNumOperands() > 3)
593 if (const Function *F = validateFunCall(STI, MRI, GR, MI))
594 GR.addForwardCall(F, &MI);
595 break;
596 case SPIRV::OpFunction:
597 // ensure there is no mismatch between actual and expected arg types:
598 // calls ahead of a processed definition
599 validateForwardCalls(STI, MRI, GR, MI);
600 break;
601
602 // ensure that LLVM IR add/sub instructions result in logical SPIR-V
603 // instructions when applied to bool type
604 case SPIRV::OpIAddS:
605 case SPIRV::OpIAddV:
606 case SPIRV::OpISubS:
607 case SPIRV::OpISubV:
608 if (GR.isScalarOrVectorOfType(MI.getOperand(1).getReg(),
609 SPIRV::OpTypeBool))
610 MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalNotEqual));
611 break;
612 // multiplication of bool operands is equivalent to a logical AND
613 case SPIRV::OpIMulS:
614 case SPIRV::OpIMulV:
615 if (GR.isScalarOrVectorOfType(MI.getOperand(1).getReg(),
616 SPIRV::OpTypeBool))
617 MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalAnd));
618 break;
619
620 // ensure that LLVM IR bitwise instructions result in logical SPIR-V
621 // instructions when applied to bool type
622 case SPIRV::OpBitwiseOrS:
623 case SPIRV::OpBitwiseOrV:
624 if (GR.isScalarOrVectorOfType(MI.getOperand(1).getReg(),
625 SPIRV::OpTypeBool))
626 MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalOr));
627 break;
628 case SPIRV::OpBitwiseAndS:
629 case SPIRV::OpBitwiseAndV:
630 if (GR.isScalarOrVectorOfType(MI.getOperand(1).getReg(),
631 SPIRV::OpTypeBool))
632 MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalAnd));
633 break;
634 case SPIRV::OpBitwiseXorS:
635 case SPIRV::OpBitwiseXorV:
636 if (GR.isScalarOrVectorOfType(MI.getOperand(1).getReg(),
637 SPIRV::OpTypeBool))
638 MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalNotEqual));
639 break;
640 case SPIRV::OpLifetimeStart:
641 case SPIRV::OpLifetimeStop:
642 if (MI.getOperand(1).getImm() > 0)
643 validateLifetimeStart(STI, MRI, GR, MI);
644 break;
645 case SPIRV::OpGroupAsyncCopy:
646 validatePtrUnwrapStructField(STI, MRI, GR, MI, 3);
647 validatePtrUnwrapStructField(STI, MRI, GR, MI, 4);
648 break;
649 case SPIRV::OpGroupWaitEvents:
650 // OpGroupWaitEvents ..., ..., <pointer to OpTypeEvent>
651 validateGroupWaitEventsPtr(STI, MRI, GR, MI);
652 break;
653 case SPIRV::OpConstantI: {
654 SPIRVTypeInst Type = GR.getSPIRVTypeForVReg(MI.getOperand(1).getReg());
655 if (Type->getOpcode() != SPIRV::OpTypeInt && MI.getOperand(2).isImm() &&
656 MI.getOperand(2).getImm() == 0) {
657 // Validate the null constant of a target extension type
658 MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpConstantNull));
659 for (unsigned i = MI.getNumOperands() - 1; i > 1; --i)
660 MI.removeOperand(i);
661 }
662 } break;
663 case SPIRV::OpExtInst: {
664 // prefetch
665 if (!MI.getOperand(2).isImm() || !MI.getOperand(3).isImm() ||
666 MI.getOperand(2).getImm() != SPIRV::InstructionSet::OpenCL_std)
667 continue;
668 switch (MI.getOperand(3).getImm()) {
669 case SPIRV::OpenCLExtInst::frexp:
670 case SPIRV::OpenCLExtInst::lgamma_r:
671 case SPIRV::OpenCLExtInst::remquo: {
672 // The last operand must be of a pointer to i32 or vector of i32
673 // values.
674 MachineIRBuilder MIB(MI);
675 SPIRVTypeInst Int32Type = GR.getOrCreateSPIRVIntegerType(32, MIB);
676 SPIRVTypeInst RetType = MRI->getVRegDef(MI.getOperand(1).getReg());
677 assert(RetType && "Expected return type");
679 STI, MRI, GR, MI, MI.getNumOperands() - 1,
680 (!isVectorType(RetType))
681 ? Int32Type
683 Int32Type, GR.getScalarOrVectorComponentCount(RetType),
684 MIB, false));
685 } break;
686 case SPIRV::OpenCLExtInst::fract:
687 case SPIRV::OpenCLExtInst::modf:
688 case SPIRV::OpenCLExtInst::sincos:
689 // The last operand must be of a pointer to the base type represented
690 // by the previous operand.
691 assert(MI.getOperand(MI.getNumOperands() - 2).isReg() &&
692 "Expected v-reg");
694 STI, MRI, GR, MI, MI.getNumOperands() - 1,
696 MI.getOperand(MI.getNumOperands() - 2).getReg()));
697 break;
698 case SPIRV::OpenCLExtInst::prefetch:
699 // Expected `ptr` type is a pointer to float, integer or vector, but
700 // the pontee value can be wrapped into a struct.
701 assert(MI.getOperand(MI.getNumOperands() - 2).isReg() &&
702 "Expected v-reg");
703 validatePtrUnwrapStructField(STI, MRI, GR, MI,
704 MI.getNumOperands() - 2);
705 break;
706 }
707 } break;
708 }
709 }
710 }
712}
713
714// Modifies either operand PtrOpIdx or OpIdx so that the pointee type of
715// PtrOpIdx matches the type for operand OpIdx. Returns true if they already
716// match or if the instruction was modified to make them match.
718 MachineInstr &I, unsigned int PtrOpIdx, unsigned int OpIdx) const {
719 SPIRVGlobalRegistry &GR = *STI.getSPIRVGlobalRegistry();
720 SPIRVTypeInst PtrType = GR.getResultType(I.getOperand(PtrOpIdx).getReg());
721
722 if (PtrType && PtrType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR)
723 return true;
724
725 SPIRVTypeInst PointeeType = GR.getPointeeType(PtrType);
726 SPIRVTypeInst OpType = GR.getResultType(I.getOperand(OpIdx).getReg());
727
728 if (PointeeType == OpType)
729 return true;
730
731 // getPointeeType yields nullptr for anything that is not an OpTypePointer.
732 // The early return above does not cover an untyped pointer nested in another
733 // type, such as a vector of pointers built for a scalarized vector GEP.
734 // typesLogicallyMatch dereferences both of its arguments, so bail out before
735 // calling it.
736 if (PointeeType && OpType && typesLogicallyMatch(PointeeType, OpType, GR)) {
737 // Apply OpCopyLogical to OpIdx.
738 if (I.getOperand(OpIdx).isDef() &&
739 insertLogicalCopyOnResult(I, PointeeType)) {
740 return true;
741 }
742
743 llvm_unreachable("Unable to add OpCopyLogical yet.");
744 return false;
745 }
746
747 return false;
748}
749
751 MachineInstr &I, SPIRVTypeInst NewResultType) const {
752 MachineRegisterInfo *MRI = &I.getMF()->getRegInfo();
753 SPIRVGlobalRegistry &GR = *STI.getSPIRVGlobalRegistry();
754
755 Register NewResultReg =
756 createVirtualRegister(NewResultType, &GR, MRI, *I.getMF());
757 Register NewTypeReg = GR.getSPIRVTypeID(NewResultType);
758
759 assert(llvm::size(I.defs()) == 1 && "Expected only one def");
760 MachineOperand &OldResult = *I.defs().begin();
761 Register OldResultReg = OldResult.getReg();
762 MachineOperand &OldType = *I.uses().begin();
763 Register OldTypeReg = OldType.getReg();
764
765 OldResult.setReg(NewResultReg);
766 OldType.setReg(NewTypeReg);
767
768 MachineIRBuilder MIB(*I.getNextNode());
769 MIB.buildInstr(SPIRV::OpCopyLogical)
770 .addDef(OldResultReg)
771 .addUse(OldTypeReg)
772 .addUse(NewResultReg)
773 .constrainAllUses(*STI.getInstrInfo(), *STI.getRegisterInfo(),
774 *STI.getRegBankInfo());
775 return true;
776}
777
794
797 // TODO: Pointer operand should be cast to integer in atomicrmw xchg, since
798 // SPIR-V only supports atomic exchange for integer and floating-point types.
800}
801
804 // TODO: pointer load should return CastToInteger, but
805 // convertAtomicLoadToIntegerType uses BitCast which asserts on pointer types.
807}
808
811 // TODO: pointer store should return CastToInteger, but
812 // convertAtomicStoreToIntegerType uses BitCast which asserts on pointer
813 // types.
815}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator MBBI
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Register const TargetRegisterInfo * TRI
static bool typesLogicallyMatch(const SPIRVTypeInst Ty1, const SPIRVTypeInst Ty2, SPIRVGlobalRegistry &GR)
static void validateVec1Ops(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &MI)
static void validateLifetimeStart(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &I)
static void validatePtrTypes(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &I, unsigned OpIdx, SPIRVTypeInst ResType, const Type *ResTy=nullptr)
static void validateGroupWaitEventsPtr(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &I)
static void validatePtrUnwrapStructField(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &I, unsigned OpIdx)
Register getTypeReg(MachineRegisterInfo *MRI, Register OpReg)
void validateAccessChain(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &I)
void validateFunCallMachineDef(const SPIRVSubtarget &STI, MachineRegisterInfo *DefMRI, MachineRegisterInfo *CallMRI, SPIRVGlobalRegistry &GR, MachineInstr &FunCall, MachineInstr *FunDef)
void validateForwardCalls(const SPIRVSubtarget &STI, MachineRegisterInfo *DefMRI, SPIRVGlobalRegistry &GR, MachineInstr &FunDef)
const Function * validateFunCall(const SPIRVSubtarget &STI, MachineRegisterInfo *CallMRI, SPIRVGlobalRegistry &GR, MachineInstr &FunCall)
static void doInsertBitcast(const SPIRVSubtarget &STI, MachineRegisterInfo *MRI, SPIRVGlobalRegistry &GR, MachineInstr &I, Register OpReg, unsigned OpIdx, SPIRVTypeInst NewPtrType)
static SPIRVTypeInst createNewPtrType(SPIRVGlobalRegistry &GR, MachineInstr &I, SPIRVTypeInst OpType, bool ReuseType, SPIRVTypeInst ResType, const Type *ResTy)
This file describes how to lower LLVM code to machine code.
an instruction that atomically reads a memory location, combines it with another value,...
@ FAdd
*p = old + v
@ FSub
*p = old - v
@ UIncWrap
Increment one up to a maximum value.
@ FMin
*p = minnum(old, v) minnum matches the behavior of llvm.minnum.
@ FMax
*p = maxnum(old, v) maxnum matches the behavior of llvm.maxnum.
@ UDecWrap
Decrement one until a minimum value or zero.
@ Nand
*p = ~(old & v)
BinOp getOperation() const
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
static constexpr ElementCount getFixed(ScalarTy MinVal)
Definition TypeSize.h:309
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:353
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
Machine Value Type.
bool isVector() const
Return true if this is a vector value type.
bool isInteger() const
Return true if this is an integer or a vector integer type.
static MVT getVectorVT(MVT VT, unsigned NumElements)
bool isFloatingPoint() const
Return true if this is a FP or a vector FP type.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
MachineInstrBundleIterator< MachineInstr > iterator
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
BasicBlockListType::iterator iterator
Helper class to build MachineInstr.
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
MachineFunction & getMF()
Getter for the function we currently build.
void constrainAllUses(const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) const
const MachineInstrBuilder & addUse(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register use operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addDef(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register definition operand.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
unsigned getNumOperands() const
Retuns the total number of operands.
const MachineOperand & getOperand(unsigned i) const
Flags
Flags values. These may be or'd together.
MachineOperand class - Representation of each machine instruction operand.
const GlobalValue * getGlobal() const
int64_t getImm() const
LLVM_ABI void setReg(Register Reg)
Change the register this operand corresponds to.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
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 ...
bool reservedRegsFrozen() const
reservedRegsFrozen - Returns true after freezeReservedRegs() was called to ensure the set of reserved...
iterator_range< use_instr_iterator > use_instructions(Register Reg) const
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
void addForwardCall(const Function *F, MachineInstr *MI)
SPIRVTypeInst getOrCreateSPIRVPointerType(const Type *BaseType, MachineIRBuilder &MIRBuilder, SPIRV::StorageClass::StorageClass SC, bool ForceTyped=false)
SPIRVTypeInst getOrCreateSPIRVIntegerType(unsigned BitWidth, MachineIRBuilder &MIRBuilder)
SPIRVTypeInst getOrCreateSPIRVVectorType(SPIRVTypeInst BaseType, unsigned NumElements, MachineIRBuilder &MIRBuilder, bool EmitIR)
SPIRVTypeInst getResultType(Register VReg, MachineFunction *MF=nullptr)
unsigned getScalarOrVectorComponentCount(Register VReg) const
const Type * getTypeForSPIRVType(SPIRVTypeInst Ty) const
bool isBitcastCompatible(SPIRVTypeInst Type1, SPIRVTypeInst Type2) const
const MachineInstr * getFunctionDefinition(const Function *F)
Register getSPIRVTypeID(SPIRVTypeInst SpirvType) const
SPIRVTypeInst getPointeeType(SPIRVTypeInst PtrType)
SmallPtrSet< MachineInstr *, 8 > * getForwardCalls(const Function *F)
SPIRVTypeInst getOrCreateSPIRVType(const Type *Type, MachineInstr &I, SPIRV::AccessQualifier::AccessQualifier AQ, bool EmitIR)
bool isScalarOrVectorOfType(Register VReg, unsigned TypeOpcode) const
MachineFunction * setCurrentFunc(MachineFunction &MF)
SPIRVTypeInst getSPIRVTypeForVReg(Register VReg, const MachineFunction *MF=nullptr) const
const Function * getFunctionByDefinition(const MachineInstr *MI)
const SPIRVInstrInfo * getInstrInfo() const override
const SPIRVRegisterInfo * getRegisterInfo() const override
const RegisterBankInfo * getRegBankInfo() const override
AtomicExpansionKind shouldCastAtomicRMWIInIR(AtomicRMWInst *RMWI) const override
Returns how the given atomic atomicrmw should be cast by the IR-level AtomicExpand pass.
AtomicExpansionKind shouldCastAtomicLoadInIR(LoadInst *LI) const override
Returns how the given (atomic) load should be cast by the IR-level AtomicExpand pass.
bool enforcePtrTypeCompatibility(MachineInstr &I, unsigned PtrOpIdx, unsigned OpIdx) const
unsigned getNumRegisters(LLVMContext &Context, EVT VT, std::optional< MVT > RegisterVT=std::nullopt) const override
Return the number of registers that this ValueType will eventually require.
unsigned getNumRegistersForCallingConv(LLVMContext &Context, CallingConv::ID CC, EVT VT) const override
Certain targets require unusual breakdowns of certain types.
MVT getRegisterTypeForCallingConv(LLVMContext &Context, CallingConv::ID CC, EVT VT) const override
Certain combinations of ABIs, Targets and features require that types are legal for some operations a...
AtomicExpansionKind shouldExpandAtomicRMWInIR(const AtomicRMWInst *RMW) const override
Returns how the IR-level AtomicExpand pass should expand the given AtomicRMW, if at all.
void finalizeLowering(MachineFunction &MF) const override
Execute target specific actions to finalize target lowering.
void getTgtMemIntrinsic(SmallVectorImpl< IntrinsicInfo > &Infos, const CallBase &I, MachineFunction &MF, unsigned Intrinsic) const override
Given an intrinsic, checks if on the target the intrinsic will need to map to a MemIntrinsicNode (tou...
bool insertLogicalCopyOnResult(MachineInstr &I, SPIRVTypeInst NewResultType) const
AtomicExpansionKind shouldCastAtomicStoreInIR(StoreInst *SI) const override
Returns how the given (atomic) store should be cast by the IR-level AtomicExpand pass into.
SPIRVTargetLowering(const TargetMachine &TM, const SPIRVSubtarget &ST)
std::pair< unsigned, const TargetRegisterClass * > getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const override
Given a physical register constraint (e.g.
ConstraintType getConstraintType(StringRef Constraint) const override
Given a constraint, return the type of constraint it is for this target.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
An instruction for storing to memory.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:258
static LLVM_ABI TargetExtType * get(LLVMContext &Context, StringRef Name, ArrayRef< Type * > Types={}, ArrayRef< unsigned > Ints={})
Return a target extension type having the specified name and optional type and integer parameters.
Definition Type.cpp:960
virtual void finalizeLowering(MachineFunction &MF) const
Execute target specific actions to finalize target lowering.
virtual AtomicExpansionKind shouldExpandAtomicRMWInIR(const AtomicRMWInst *RMW) const
Returns how the IR-level AtomicExpand pass should expand the given AtomicRMW, if at all.
MVT getRegisterType(LLVMContext &Context, EVT VT) const
Return the type of registers that this ValueType will eventually require.
void setMaxAtomicSizeInBitsSupported(unsigned SizeInBits)
Set the maximum atomic operation size supported by the backend.
void setMinCmpXchgSizeInBits(unsigned SizeInBits)
Sets the minimum cmpxchg or ll/sc size supported by the backend.
AtomicExpansionKind
Enum that specifies what an atomic load/AtomicRMWInst is expanded to, if at all.
TargetLowering(const TargetLowering &)=delete
Primary interface to the complete machine description for the target machine.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:307
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
This namespace contains an enum with a value for every intrinsic/builtin function known by LLVM.
This is an optimization pass for GlobalISel generic memory operations.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1669
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
MachineInstr * getDef(const MachineOperand &MO, const MachineRegisterInfo *MRI)
Register createVirtualRegister(SPIRVTypeInst SpvType, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI, const MachineFunction &MF)
bool isVectorType(SPIRVTypeInst SPVTy)
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
AtomicOrdering
Atomic ordering for LLVM's memory model.
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
Extended Value Type.
Definition ValueTypes.h:35
TypeSize getSizeInBits() const
Return the size of the specified value type in bits.
Definition ValueTypes.h:396
MVT getSimpleVT() const
Return the SimpleValueType held in the specified simple EVT.
Definition ValueTypes.h:339
bool isVector() const
Return true if this is a vector value type.
Definition ValueTypes.h:176
EVT getVectorElementType() const
Given a vector type, return the type of each element.
Definition ValueTypes.h:351
unsigned getVectorNumElements() const
Given a vector type, return the number of elements it contains.
Definition ValueTypes.h:359
bool isInteger() const
Return true if this is an integer or a vector integer type.
Definition ValueTypes.h:160