27#include "llvm/IR/IntrinsicsSPIRV.h"
35#include <unordered_set>
59 cl::desc(
"Emit OpName for all instructions"),
63#define GET_BuiltinGroup_DECL
64#include "SPIRVGenTables.inc"
69class GlobalVariableUsers {
70 template <
typename T1,
typename T2>
71 using OneToManyMapTy = DenseMap<T1, SmallPtrSet<T2, 4>>;
73 OneToManyMapTy<const GlobalVariable *, const Function *> GlobalIsUsedByFun;
75 void collectGlobalUsers(
76 const GlobalVariable *GV,
77 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
78 &GlobalIsUsedByGlobal) {
80 while (!
Stack.empty()) {
84 GlobalIsUsedByFun[GV].insert(
I->getFunction());
89 GlobalIsUsedByGlobal[GV].insert(UserGV);
94 Stack.append(
C->user_begin(),
C->user_end());
98 bool propagateGlobalToGlobalUsers(
99 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
100 &GlobalIsUsedByGlobal) {
103 for (
auto &[GV, UserGlobals] : GlobalIsUsedByGlobal) {
104 OldUsersGlobals.
assign(UserGlobals.begin(), UserGlobals.end());
105 for (
const GlobalVariable *UserGV : OldUsersGlobals) {
106 auto It = GlobalIsUsedByGlobal.find(UserGV);
107 if (It == GlobalIsUsedByGlobal.end())
115 void propagateGlobalToFunctionReferences(
116 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
117 &GlobalIsUsedByGlobal) {
118 for (
auto &[GV, UserGlobals] : GlobalIsUsedByGlobal) {
119 auto &UserFunctions = GlobalIsUsedByFun[GV];
120 for (
const GlobalVariable *UserGV : UserGlobals) {
121 auto It = GlobalIsUsedByFun.find(UserGV);
122 if (It == GlobalIsUsedByFun.end())
133 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
134 GlobalIsUsedByGlobal;
135 GlobalIsUsedByFun.clear();
136 for (GlobalVariable &GV :
M.globals())
137 collectGlobalUsers(&GV, GlobalIsUsedByGlobal);
140 while (propagateGlobalToGlobalUsers(GlobalIsUsedByGlobal))
143 propagateGlobalToFunctionReferences(GlobalIsUsedByGlobal);
146 using FunctionSetType =
typename decltype(GlobalIsUsedByFun)::mapped_type;
147 const FunctionSetType &
148 getTransitiveUserFunctions(
const GlobalVariable &GV)
const {
149 auto It = GlobalIsUsedByFun.find(&GV);
150 if (It != GlobalIsUsedByFun.end())
153 static const FunctionSetType
Empty{};
158static bool isaGEP(
const Value *V) {
162class SPIRVEmitIntrinsics
164 public InstVisitor<SPIRVEmitIntrinsics, Instruction *> {
165 const SPIRVTargetMachine &TM;
166 SPIRVGlobalRegistry *GR =
nullptr;
168 bool TrackConstants =
true;
169 bool HaveFunPtrs =
false;
170 DenseMap<Instruction *, Constant *> AggrConsts;
171 DenseMap<Instruction *, Type *> AggrConstTypes;
172 DenseSet<Instruction *> AggrStores;
173 SmallPtrSet<Instruction *, 8> DeletedInstrs;
174 GlobalVariableUsers GVUsers;
175 std::unordered_set<Value *> Named;
178 DenseMap<Function *, SmallVector<std::pair<unsigned, Type *>>> FDeclPtrTys;
181 bool CanTodoType =
true;
182 unsigned TodoTypeSz = 0;
183 DenseMap<Value *, bool> TodoType;
184 void insertTodoType(
Value *
Op) {
186 if (CanTodoType && !isaGEP(
Op)) {
187 auto It = TodoType.try_emplace(
Op,
true);
192 void eraseTodoType(
Value *
Op) {
193 auto It = TodoType.find(
Op);
194 if (It != TodoType.end() && It->second) {
202 auto It = TodoType.find(
Op);
203 return It != TodoType.end() && It->second;
207 std::unordered_set<Instruction *> TypeValidated;
210 enum WellKnownTypes { Event };
213 Type *deduceElementType(
Value *
I,
bool UnknownElemTypeI8);
214 Type *deduceElementTypeHelper(
Value *
I,
bool UnknownElemTypeI8);
215 Type *deduceElementTypeHelper(
Value *
I, std::unordered_set<Value *> &Visited,
216 bool UnknownElemTypeI8,
217 bool IgnoreKnownType =
false);
218 Type *deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
219 bool UnknownElemTypeI8);
220 Type *deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
221 std::unordered_set<Value *> &Visited,
222 bool UnknownElemTypeI8);
224 std::unordered_set<Value *> &Visited,
225 bool UnknownElemTypeI8);
227 bool UnknownElemTypeI8);
230 Type *deduceNestedTypeHelper(User *U,
bool UnknownElemTypeI8);
231 Type *deduceNestedTypeHelper(User *U,
Type *Ty,
232 std::unordered_set<Value *> &Visited,
233 bool UnknownElemTypeI8);
236 void deduceOperandElementType(Instruction *
I,
237 SmallPtrSet<Instruction *, 4> *IncompleteRets,
238 const SmallPtrSet<Value *, 4> *AskOps =
nullptr,
239 bool IsPostprocessing =
false);
244 Type *reconstructType(
Value *
Op,
bool UnknownElemTypeI8,
245 bool IsPostprocessing);
247 void replaceMemInstrUses(Instruction *Old, Instruction *New,
IRBuilder<> &
B);
249 bool insertAssignPtrTypeIntrs(Instruction *
I,
IRBuilder<> &
B,
250 bool UnknownElemTypeI8);
252 void insertAssignPtrTypeTargetExt(TargetExtType *AssignedType,
Value *V,
254 void replacePointerOperandWithPtrCast(Instruction *
I,
Value *Pointer,
255 Type *ExpectedElementType,
256 unsigned OperandToReplace,
258 void insertPtrCastOrAssignTypeInstr(Instruction *
I,
IRBuilder<> &
B);
259 bool shouldTryToAddMemAliasingDecoration(Instruction *Inst);
261 void insertConstantsForFPFastMathDefault(
Module &M);
263 void processGlobalValue(GlobalVariable &GV,
IRBuilder<> &
B);
265 void processParamTypesByFunHeader(Function *
F,
IRBuilder<> &
B);
266 Type *deduceFunParamElementType(Function *
F,
unsigned OpIdx);
267 Type *deduceFunParamElementType(Function *
F,
unsigned OpIdx,
268 std::unordered_set<Function *> &FVisited);
270 bool deduceOperandElementTypeCalledFunction(
272 Type *&KnownElemTy,
bool &Incomplete);
273 void deduceOperandElementTypeFunctionPointer(
275 Type *&KnownElemTy,
bool IsPostprocessing);
276 bool deduceOperandElementTypeFunctionRet(
277 Instruction *
I, SmallPtrSet<Instruction *, 4> *IncompleteRets,
278 const SmallPtrSet<Value *, 4> *AskOps,
bool IsPostprocessing,
281 CallInst *buildSpvPtrcast(Function *
F,
Value *
Op,
Type *ElemTy);
282 void replaceUsesOfWithSpvPtrcast(
Value *
Op,
Type *ElemTy, Instruction *
I,
283 DenseMap<Function *, CallInst *> Ptrcasts);
285 DenseSet<std::pair<Value *, Value *>> &VisitedSubst);
288 DenseSet<std::pair<Value *, Value *>> &VisitedSubst);
289 void propagateElemTypeRec(
Value *
Op,
Type *PtrElemTy,
Type *CastElemTy,
290 DenseSet<std::pair<Value *, Value *>> &VisitedSubst,
291 std::unordered_set<Value *> &Visited,
292 DenseMap<Function *, CallInst *> Ptrcasts);
295 void replaceAllUsesWithAndErase(
IRBuilder<> &
B, Instruction *Src,
296 Instruction *Dest,
bool DeleteOld =
true);
300 GetElementPtrInst *simplifyZeroLengthArrayGepInst(GetElementPtrInst *
GEP);
303 bool postprocessTypes(
Module &M);
304 bool processFunctionPointers(
Module &M);
305 void parseFunDeclarations(
Module &M);
306 void useRoundingMode(ConstrainedFPIntrinsic *FPI,
IRBuilder<> &
B);
307 bool processMaskedMemIntrinsic(IntrinsicInst &
I);
308 bool convertMaskedMemIntrinsics(
Module &M);
309 void preprocessBoolVectorBitcasts(Function &
F);
311 void emitUnstructuredLoopControls(Function &
F,
IRBuilder<> &
B);
327 bool walkLogicalAccessChain(
328 GetElementPtrInst &
GEP,
329 const std::function<
void(
Type *PointedType, uint64_t Index)>
338 Type *getGEPType(GetElementPtrInst *
GEP);
345 Type *getGEPTypeLogical(GetElementPtrInst *
GEP);
347 Instruction *buildLogicalAccessChainFromGEP(GetElementPtrInst &
GEP);
351 SPIRVEmitIntrinsics(
const SPIRVTargetMachine &TM) : ModulePass(ID), TM(TM) {}
354 Instruction *visitGetElementPtrInst(GetElementPtrInst &
I);
357 Instruction *visitInsertElementInst(InsertElementInst &
I);
358 Instruction *visitExtractElementInst(ExtractElementInst &
I);
360 Instruction *visitExtractValueInst(ExtractValueInst &
I);
364 Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &
I);
368 StringRef getPassName()
const override {
return "SPIRV emit intrinsics"; }
370 bool runOnModule(
Module &M)
override;
372 void getAnalysisUsage(AnalysisUsage &AU)
const override {
373 ModulePass::getAnalysisUsage(AU);
382 return II->getIntrinsicID() == Intrinsic::experimental_convergence_entry ||
383 II->getIntrinsicID() == Intrinsic::experimental_convergence_loop ||
384 II->getIntrinsicID() == Intrinsic::experimental_convergence_anchor;
387bool expectIgnoredInIRTranslation(
const Instruction *
I) {
391 switch (
II->getIntrinsicID()) {
392 case Intrinsic::invariant_start:
393 case Intrinsic::spv_resource_handlefrombinding:
394 case Intrinsic::spv_resource_getpointer:
404 if (
II->getIntrinsicID() == Intrinsic::spv_ptrcast) {
405 Value *V =
II->getArgOperand(0);
406 return getPointerRoot(V);
414char SPIRVEmitIntrinsics::ID = 0;
417 "SPIRV emit intrinsics",
false,
false)
432 bool IsUndefAggregate =
isa<UndefValue>(V) && V->getType()->isAggregateType();
439 B.SetInsertPoint(
I->getParent()->getFirstNonPHIOrDbgOrAlloca());
445 B.SetCurrentDebugLocation(
I->getDebugLoc());
446 if (
I->getType()->isVoidTy())
447 B.SetInsertPoint(
I->getNextNode());
449 B.SetInsertPoint(*
I->getInsertionPointAfterDef());
454 switch (Intr->getIntrinsicID()) {
455 case Intrinsic::invariant_start:
456 case Intrinsic::invariant_end:
464 if (
I->getType()->isTokenTy())
466 "does not support token type",
471 if (!
I->hasName() ||
I->getType()->isAggregateType() ||
472 expectIgnoredInIRTranslation(
I))
483 if (
F &&
F->getName().starts_with(
"llvm.spv.alloca"))
494 std::vector<Value *> Args = {
497 B.CreateIntrinsic(Intrinsic::spv_assign_name, {
I->getType()}, Args);
500void SPIRVEmitIntrinsics::replaceAllUsesWith(
Value *Src,
Value *Dest,
504 if (isTodoType(Src)) {
507 insertTodoType(Dest);
511void SPIRVEmitIntrinsics::replaceAllUsesWithAndErase(
IRBuilder<> &
B,
516 std::string
Name = Src->hasName() ? Src->getName().str() :
"";
517 Src->eraseFromParent();
520 if (Named.insert(Dest).second)
545Type *SPIRVEmitIntrinsics::reconstructType(
Value *
Op,
bool UnknownElemTypeI8,
546 bool IsPostprocessing) {
561 if (UnknownElemTypeI8) {
562 if (!IsPostprocessing)
570CallInst *SPIRVEmitIntrinsics::buildSpvPtrcast(Function *
F,
Value *
Op,
578 B.SetInsertPointPastAllocas(OpA->getParent());
581 B.SetInsertPoint(
F->getEntryBlock().getFirstNonPHIOrDbgOrAlloca());
583 Type *OpTy =
Op->getType();
587 CallInst *PtrCasted =
588 B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
593void SPIRVEmitIntrinsics::replaceUsesOfWithSpvPtrcast(
595 DenseMap<Function *, CallInst *> Ptrcasts) {
597 CallInst *PtrCastedI =
nullptr;
598 auto It = Ptrcasts.
find(
F);
599 if (It == Ptrcasts.
end()) {
600 PtrCastedI = buildSpvPtrcast(
F,
Op, ElemTy);
601 Ptrcasts[
F] = PtrCastedI;
603 PtrCastedI = It->second;
605 I->replaceUsesOfWith(
Op, PtrCastedI);
608void SPIRVEmitIntrinsics::propagateElemType(
610 DenseSet<std::pair<Value *, Value *>> &VisitedSubst) {
611 DenseMap<Function *, CallInst *> Ptrcasts;
613 for (
auto *U :
Users) {
616 if (!VisitedSubst.insert(std::make_pair(U,
Op)).second)
621 if (isaGEP(UI) || TypeValidated.find(UI) != TypeValidated.end())
622 replaceUsesOfWithSpvPtrcast(
Op, ElemTy, UI, Ptrcasts);
626void SPIRVEmitIntrinsics::propagateElemTypeRec(
628 DenseSet<std::pair<Value *, Value *>> &VisitedSubst) {
629 std::unordered_set<Value *> Visited;
630 DenseMap<Function *, CallInst *> Ptrcasts;
631 propagateElemTypeRec(
Op, PtrElemTy, CastElemTy, VisitedSubst, Visited,
632 std::move(Ptrcasts));
635void SPIRVEmitIntrinsics::propagateElemTypeRec(
637 DenseSet<std::pair<Value *, Value *>> &VisitedSubst,
638 std::unordered_set<Value *> &Visited,
639 DenseMap<Function *, CallInst *> Ptrcasts) {
640 if (!Visited.insert(
Op).second)
643 for (
auto *U :
Users) {
646 if (!VisitedSubst.insert(std::make_pair(U,
Op)).second)
651 if (isaGEP(UI) || TypeValidated.find(UI) != TypeValidated.end())
652 replaceUsesOfWithSpvPtrcast(
Op, CastElemTy, UI, Ptrcasts);
660SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
661 bool UnknownElemTypeI8) {
662 std::unordered_set<Value *> Visited;
663 return deduceElementTypeByValueDeep(ValueTy, Operand, Visited,
667Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
668 Type *ValueTy,
Value *Operand, std::unordered_set<Value *> &Visited,
669 bool UnknownElemTypeI8) {
674 deduceElementTypeHelper(Operand, Visited, UnknownElemTypeI8))
685Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
686 Value *
Op, std::unordered_set<Value *> &Visited,
bool UnknownElemTypeI8) {
698 for (User *OpU :
Op->users()) {
700 if (
Type *Ty = deduceElementTypeHelper(Inst, Visited, UnknownElemTypeI8))
713 if ((DemangledName.
starts_with(
"__spirv_ocl_printf(") ||
722Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
Value *
I,
723 bool UnknownElemTypeI8) {
724 std::unordered_set<Value *> Visited;
725 return deduceElementTypeHelper(
I, Visited, UnknownElemTypeI8);
728void SPIRVEmitIntrinsics::maybeAssignPtrType(
Type *&Ty,
Value *
Op,
Type *RefTy,
729 bool UnknownElemTypeI8) {
731 if (!UnknownElemTypeI8)
738bool SPIRVEmitIntrinsics::walkLogicalAccessChain(
739 GetElementPtrInst &
GEP,
740 const std::function<
void(
Type *, uint64_t)> &OnLiteralIndexing,
741 const std::function<
void(
Type *,
Value *)> &OnDynamicIndexing) {
749 Value *Src = getPointerRoot(
GEP.getPointerOperand());
750 Type *CurType = deduceElementType(Src,
true);
759 OnDynamicIndexing(AT->getElementType(), Operand);
760 return AT ==
nullptr;
768 uint32_t EltTypeSize =
DL.getTypeSizeInBits(AT->getElementType()) / 8;
772 CurType = AT->getElementType();
773 OnLiteralIndexing(CurType, Index);
775 uint32_t StructSize =
DL.getTypeSizeInBits(ST) / 8;
778 const auto &STL =
DL.getStructLayout(ST);
779 unsigned Element = STL->getElementContainingOffset(
Offset);
780 Offset -= STL->getElementOffset(Element);
781 CurType =
ST->getElementType(Element);
782 OnLiteralIndexing(CurType, Element);
784 Type *EltTy = VT->getElementType();
785 TypeSize EltSizeBits =
DL.getTypeSizeInBits(EltTy);
786 assert(EltSizeBits % 8 == 0 &&
787 "Element type size in bits must be a multiple of 8.");
788 uint32_t EltTypeSize = EltSizeBits / 8;
793 OnLiteralIndexing(CurType, Index);
805SPIRVEmitIntrinsics::buildLogicalAccessChainFromGEP(GetElementPtrInst &
GEP) {
808 B.SetInsertPoint(&
GEP);
810 std::vector<Value *> Indices;
811 Indices.push_back(ConstantInt::get(
812 IntegerType::getInt32Ty(CurrF->
getContext()), 0,
false));
813 walkLogicalAccessChain(
815 [&Indices, &
B](
Type *EltType, uint64_t Index) {
817 ConstantInt::get(
B.getInt64Ty(), Index,
false));
820 uint32_t EltTypeSize =
DL.getTypeSizeInBits(EltType) / 8;
822 Offset, ConstantInt::get(
Offset->getType(), EltTypeSize,
824 Indices.push_back(Index);
828 SmallVector<Value *, 4>
Args;
829 Args.push_back(
B.getInt1(
GEP.isInBounds()));
830 Args.push_back(
GEP.getOperand(0));
832 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
833 replaceAllUsesWithAndErase(
B, &
GEP, NewI);
837Type *SPIRVEmitIntrinsics::getGEPTypeLogical(GetElementPtrInst *
GEP) {
839 Type *CurType =
GEP->getResultElementType();
841 bool Interrupted = walkLogicalAccessChain(
842 *
GEP, [&CurType](
Type *EltType, uint64_t Index) { CurType = EltType; },
845 return Interrupted ?
GEP->getResultElementType() : CurType;
848Type *SPIRVEmitIntrinsics::getGEPType(GetElementPtrInst *
Ref) {
849 if (
Ref->getSourceElementType() ==
850 IntegerType::getInt8Ty(CurrF->
getContext()) &&
852 return getGEPTypeLogical(
Ref);
859 Ty =
Ref->getSourceElementType();
863 Ty =
Ref->getResultElementType();
868Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
869 Value *
I, std::unordered_set<Value *> &Visited,
bool UnknownElemTypeI8,
870 bool IgnoreKnownType) {
876 if (!IgnoreKnownType)
881 if (!Visited.insert(
I).second)
888 maybeAssignPtrType(Ty,
I,
Ref->getAllocatedType(), UnknownElemTypeI8);
890 Ty = getGEPType(
Ref);
892 Ty = SGEP->getResultElementType();
897 KnownTy =
Op->getType();
899 maybeAssignPtrType(Ty,
I, ElemTy, UnknownElemTypeI8);
902 Ty = SPIRV::getOriginalFunctionType(*Fn);
905 Ty = deduceElementTypeByValueDeep(
907 Ref->getNumOperands() > 0 ?
Ref->getOperand(0) :
nullptr, Visited,
911 Type *RefTy = deduceElementTypeHelper(
Ref->getPointerOperand(), Visited,
913 maybeAssignPtrType(Ty,
I, RefTy, UnknownElemTypeI8);
915 maybeAssignPtrType(Ty,
I,
Ref->getDestTy(), UnknownElemTypeI8);
917 if (
Type *Src =
Ref->getSrcTy(), *Dest =
Ref->getDestTy();
919 Ty = deduceElementTypeHelper(
Ref->getOperand(0), Visited,
924 Ty = deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8);
928 Ty = deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8);
930 Type *BestTy =
nullptr;
932 DenseMap<Type *, unsigned> PhiTys;
933 for (
int i =
Ref->getNumIncomingValues() - 1; i >= 0; --i) {
934 Ty = deduceElementTypeByUsersDeep(
Ref->getIncomingValue(i), Visited,
941 if (It.first->second > MaxN) {
942 MaxN = It.first->second;
950 for (
Value *
Op : {
Ref->getTrueValue(),
Ref->getFalseValue()}) {
951 Ty = deduceElementTypeByUsersDeep(
Op, Visited, UnknownElemTypeI8);
956 static StringMap<unsigned> ResTypeByArg = {
960 {
"__spirv_GenericCastToPtr_ToGlobal", 0},
961 {
"__spirv_GenericCastToPtr_ToLocal", 0},
962 {
"__spirv_GenericCastToPtr_ToPrivate", 0},
963 {
"__spirv_GenericCastToPtrExplicit_ToGlobal", 0},
964 {
"__spirv_GenericCastToPtrExplicit_ToLocal", 0},
965 {
"__spirv_GenericCastToPtrExplicit_ToPrivate", 0}};
969 if (
II &&
II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
971 if (HandleType->getTargetExtName() ==
"spirv.Image" ||
972 HandleType->getTargetExtName() ==
"spirv.SignedImage") {
973 for (User *U :
II->users()) {
978 }
else if (HandleType->getTargetExtName() ==
"spirv.VulkanBuffer") {
980 Ty = HandleType->getTypeParameter(0);
992 }
else if (
II &&
II->getIntrinsicID() ==
993 Intrinsic::spv_generic_cast_to_ptr_explicit) {
994 Ty = deduceElementTypeHelper(CI->getArgOperand(0), Visited,
996 }
else if (Function *CalledF = CI->getCalledFunction()) {
997 std::string DemangledName =
999 if (DemangledName.length() > 0)
1000 DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName);
1001 auto AsArgIt = ResTypeByArg.
find(DemangledName);
1002 if (AsArgIt != ResTypeByArg.
end())
1003 Ty = deduceElementTypeHelper(CI->getArgOperand(AsArgIt->second),
1004 Visited, UnknownElemTypeI8);
1011 if (Ty && !IgnoreKnownType) {
1022Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U,
1023 bool UnknownElemTypeI8) {
1024 std::unordered_set<Value *> Visited;
1025 return deduceNestedTypeHelper(U,
U->getType(), Visited, UnknownElemTypeI8);
1028Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
1029 User *U,
Type *OrigTy, std::unordered_set<Value *> &Visited,
1030 bool UnknownElemTypeI8) {
1039 if (!Visited.insert(U).second)
1044 bool Change =
false;
1045 for (
unsigned i = 0; i <
U->getNumOperands(); ++i) {
1047 assert(
Op &&
"Operands should not be null.");
1048 Type *OpTy =
Op->getType();
1051 if (
Type *NestedTy =
1052 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
1059 Change |= Ty != OpTy;
1067 if (
Value *
Op =
U->getNumOperands() > 0 ?
U->getOperand(0) :
nullptr) {
1068 Type *OpTy = ArrTy->getElementType();
1071 if (
Type *NestedTy =
1072 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
1079 Type *NewTy = ArrayType::get(Ty, ArrTy->getNumElements());
1085 if (
Value *
Op =
U->getNumOperands() > 0 ?
U->getOperand(0) :
nullptr) {
1086 Type *OpTy = VecTy->getElementType();
1089 if (
Type *NestedTy =
1090 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
1097 Type *NewTy = VectorType::get(Ty, VecTy->getElementCount());
1107Type *SPIRVEmitIntrinsics::deduceElementType(
Value *
I,
bool UnknownElemTypeI8) {
1108 if (
Type *Ty = deduceElementTypeHelper(
I, UnknownElemTypeI8))
1110 if (!UnknownElemTypeI8)
1113 return IntegerType::getInt8Ty(
I->getContext());
1117 Value *PointerOperand) {
1123 return I->getType();
1131bool SPIRVEmitIntrinsics::deduceOperandElementTypeCalledFunction(
1133 Type *&KnownElemTy,
bool &Incomplete) {
1137 std::string DemangledName =
1139 if (DemangledName.length() > 0 &&
1141 const SPIRVSubtarget &
ST = TM.
getSubtarget<SPIRVSubtarget>(*CalledF);
1142 auto [Grp, Opcode, ExtNo] = SPIRV::mapBuiltinToOpcode(
1143 DemangledName,
ST.getPreferredInstructionSet());
1144 if (Opcode == SPIRV::OpGroupAsyncCopy) {
1145 for (
unsigned i = 0, PtrCnt = 0; i < CI->
arg_size() && PtrCnt < 2; ++i) {
1151 KnownElemTy = ElemTy;
1152 Ops.push_back(std::make_pair(
Op, i));
1154 }
else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) {
1161 case SPIRV::OpAtomicFAddEXT:
1162 case SPIRV::OpAtomicFMinEXT:
1163 case SPIRV::OpAtomicFMaxEXT:
1164 case SPIRV::OpAtomicLoad:
1165 case SPIRV::OpAtomicCompareExchangeWeak:
1166 case SPIRV::OpAtomicCompareExchange:
1167 case SPIRV::OpAtomicExchange:
1168 case SPIRV::OpAtomicIAdd:
1169 case SPIRV::OpAtomicISub:
1170 case SPIRV::OpAtomicOr:
1171 case SPIRV::OpAtomicXor:
1172 case SPIRV::OpAtomicAnd:
1173 case SPIRV::OpAtomicUMin:
1174 case SPIRV::OpAtomicUMax:
1175 case SPIRV::OpAtomicSMin:
1176 case SPIRV::OpAtomicSMax: {
1181 Incomplete = isTodoType(
Op);
1182 Ops.push_back(std::make_pair(
Op, 0));
1184 case SPIRV::OpAtomicStore: {
1193 Incomplete = isTodoType(
Op);
1194 Ops.push_back(std::make_pair(
Op, 0));
1203void SPIRVEmitIntrinsics::deduceOperandElementTypeFunctionPointer(
1205 Type *&KnownElemTy,
bool IsPostprocessing) {
1209 Ops.push_back(std::make_pair(
Op, std::numeric_limits<unsigned>::max()));
1210 FunctionType *FTy = SPIRV::getOriginalFunctionType(*CI);
1211 bool IsNewFTy =
false, IsIncomplete =
false;
1214 Type *ArgTy = Arg->getType();
1219 if (isTodoType(Arg))
1220 IsIncomplete =
true;
1222 IsIncomplete =
true;
1225 ArgTy = FTy->getFunctionParamType(ParmIdx);
1229 Type *RetTy = FTy->getReturnType();
1236 IsIncomplete =
true;
1238 IsIncomplete =
true;
1241 if (!IsPostprocessing && IsIncomplete)
1244 IsNewFTy ? FunctionType::get(RetTy, ArgTys, FTy->isVarArg()) : FTy;
1247bool SPIRVEmitIntrinsics::deduceOperandElementTypeFunctionRet(
1248 Instruction *
I, SmallPtrSet<Instruction *, 4> *IncompleteRets,
1249 const SmallPtrSet<Value *, 4> *AskOps,
bool IsPostprocessing,
1261 DenseSet<std::pair<Value *, Value *>> VisitedSubst{std::make_pair(
I,
Op)};
1262 for (User *U :
F->users()) {
1270 propagateElemType(CI, PrevElemTy, VisitedSubst);
1280 for (Instruction *IncompleteRetI : *IncompleteRets)
1281 deduceOperandElementType(IncompleteRetI,
nullptr, AskOps,
1283 }
else if (IncompleteRets) {
1286 TypeValidated.insert(
I);
1294void SPIRVEmitIntrinsics::deduceOperandElementType(
1295 Instruction *
I, SmallPtrSet<Instruction *, 4> *IncompleteRets,
1296 const SmallPtrSet<Value *, 4> *AskOps,
bool IsPostprocessing) {
1298 Type *KnownElemTy =
nullptr;
1299 bool Incomplete =
false;
1305 Incomplete = isTodoType(
I);
1306 for (
unsigned i = 0; i <
Ref->getNumIncomingValues(); i++) {
1309 Ops.push_back(std::make_pair(
Op, i));
1315 Incomplete = isTodoType(
I);
1316 Ops.push_back(std::make_pair(
Ref->getPointerOperand(), 0));
1323 Incomplete = isTodoType(
I);
1324 Ops.push_back(std::make_pair(
Ref->getOperand(0), 0));
1328 KnownElemTy =
Ref->getSourceElementType();
1329 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1334 KnownElemTy =
Ref->getBaseType();
1335 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1338 KnownElemTy =
I->getType();
1344 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1348 reconstructType(
Ref->getValueOperand(),
false, IsPostprocessing)))
1353 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1361 Incomplete = isTodoType(
Ref->getPointerOperand());
1362 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1370 Incomplete = isTodoType(
Ref->getPointerOperand());
1371 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1377 Incomplete = isTodoType(
I);
1378 for (
unsigned i = 0; i <
Ref->getNumOperands(); i++) {
1381 Ops.push_back(std::make_pair(
Op, i));
1389 if (deduceOperandElementTypeFunctionRet(
I, IncompleteRets, AskOps,
1390 IsPostprocessing, KnownElemTy,
Op,
1393 Incomplete = isTodoType(CurrF);
1394 Ops.push_back(std::make_pair(
Op, 0));
1400 bool Incomplete0 = isTodoType(Op0);
1401 bool Incomplete1 = isTodoType(Op1);
1403 Type *ElemTy0 = (Incomplete0 && !Incomplete1 && ElemTy1)
1405 : GR->findDeducedElementType(Op0);
1407 KnownElemTy = ElemTy0;
1408 Incomplete = Incomplete0;
1409 Ops.push_back(std::make_pair(Op1, 1));
1410 }
else if (ElemTy1) {
1411 KnownElemTy = ElemTy1;
1412 Incomplete = Incomplete1;
1413 Ops.push_back(std::make_pair(Op0, 0));
1417 deduceOperandElementTypeCalledFunction(CI,
Ops, KnownElemTy, Incomplete);
1418 else if (HaveFunPtrs)
1419 deduceOperandElementTypeFunctionPointer(CI,
Ops, KnownElemTy,
1424 if (!KnownElemTy ||
Ops.size() == 0)
1429 for (
auto &OpIt :
Ops) {
1433 Type *AskTy =
nullptr;
1434 CallInst *AskCI =
nullptr;
1435 if (IsPostprocessing && AskOps) {
1441 if (Ty == KnownElemTy)
1444 Type *OpTy =
Op->getType();
1445 if (
Op->hasUseList() &&
1452 else if (!IsPostprocessing)
1456 if (AssignCI ==
nullptr) {
1465 DenseSet<std::pair<Value *, Value *>> VisitedSubst{
1466 std::make_pair(
I,
Op)};
1467 propagateElemTypeRec(
Op, KnownElemTy, PrevElemTy, VisitedSubst);
1471 CallInst *PtrCastI =
1472 buildSpvPtrcast(
I->getParent()->getParent(),
Op, KnownElemTy);
1473 if (OpIt.second == std::numeric_limits<unsigned>::max())
1476 I->setOperand(OpIt.second, PtrCastI);
1479 TypeValidated.insert(
I);
1482void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old,
1487 if (isAssignTypeInstr(U)) {
1488 B.SetInsertPoint(U);
1489 SmallVector<Value *, 2>
Args = {
New,
U->getOperand(1)};
1490 CallInst *AssignCI =
1491 B.CreateIntrinsic(Intrinsic::spv_assign_type, {
New->getType()},
Args);
1493 U->eraseFromParent();
1496 U->replaceUsesOfWith(Old, New);
1498 if (
Phi->getType() !=
New->getType()) {
1499 Phi->mutateType(
New->getType());
1500 Phi->replaceUsesOfWith(Old, New);
1503 for (User *PhiUser :
Phi->users())
1506 for (ExtractValueInst *EV : EVUsers) {
1507 B.SetInsertPoint(EV);
1509 for (
unsigned Idx : EV->indices())
1510 Args.push_back(
B.getInt32(Idx));
1512 B.CreateIntrinsic(Intrinsic::spv_extractv, {EV->getType()},
Args);
1513 EV->replaceAllUsesWith(NewEV);
1514 DeletedInstrs.
insert(EV);
1515 EV->eraseFromParent();
1518 Phi->replaceUsesOfWith(Old, New);
1524 New->copyMetadata(*Old);
1528void SPIRVEmitIntrinsics::preprocessUndefs(
IRBuilder<> &
B) {
1529 std::queue<Instruction *> Worklist;
1533 while (!Worklist.empty()) {
1535 bool BPrepared =
false;
1538 for (
auto &
Op :
I->operands()) {
1540 if (!AggrUndef || !
Op->getType()->isAggregateType())
1547 auto *IntrUndef =
B.CreateIntrinsic(Intrinsic::spv_undef, {});
1548 Worklist.push(IntrUndef);
1549 I->replaceUsesOfWith(
Op, IntrUndef);
1550 AggrConsts[IntrUndef] = AggrUndef;
1551 AggrConstTypes[IntrUndef] = AggrUndef->getType();
1556void SPIRVEmitIntrinsics::preprocessCompositeConstants(
IRBuilder<> &
B) {
1557 std::queue<Instruction *> Worklist;
1561 while (!Worklist.empty()) {
1562 auto *
I = Worklist.front();
1565 bool KeepInst =
false;
1566 for (
const auto &
Op :
I->operands()) {
1568 Type *ResTy =
nullptr;
1571 ResTy = COp->getType();
1583 ResTy =
Op->getType()->isVectorTy() ? COp->getType() :
B.getInt32Ty();
1588 for (
unsigned i = 0; i < COp->getNumElements(); ++i)
1589 Args.push_back(COp->getElementAsConstant(i));
1593 IsPhi ?
B.SetInsertPointPastAllocas(
I->getParent()->getParent())
1594 :
B.SetInsertPoint(
I);
1598 B.CreateIntrinsic(Intrinsic::spv_const_composite, {ResTy}, {
Args});
1602 AggrConsts[CI] = AggrConst;
1603 AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst,
false);
1615 B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {
I->getType()},
1620 unsigned RoundingModeDeco,
1627 ConstantInt::get(
Int32Ty, SPIRV::Decoration::FPRoundingMode)),
1636 MDNode *SaturatedConversionNode =
1638 Int32Ty, SPIRV::Decoration::SaturatedConversion))});
1645 if (Fu->isIntrinsic()) {
1646 unsigned const int IntrinsicId = Fu->getIntrinsicID();
1647 switch (IntrinsicId) {
1648 case Intrinsic::fptosi_sat:
1649 case Intrinsic::fptoui_sat:
1668 MDString *ConstraintString =
MDString::get(Ctx,
IA->getConstraintString());
1676 B.SetInsertPoint(&
Call);
1677 B.CreateIntrinsic(Intrinsic::spv_inline_asm, {
Args});
1682void SPIRVEmitIntrinsics::useRoundingMode(ConstrainedFPIntrinsic *FPI,
1685 if (!
RM.has_value())
1687 unsigned RoundingModeDeco = std::numeric_limits<unsigned>::max();
1688 switch (
RM.value()) {
1692 case RoundingMode::NearestTiesToEven:
1693 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTE;
1695 case RoundingMode::TowardNegative:
1696 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTN;
1698 case RoundingMode::TowardPositive:
1699 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTP;
1701 case RoundingMode::TowardZero:
1702 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTZ;
1704 case RoundingMode::Dynamic:
1705 case RoundingMode::NearestTiesToAway:
1709 if (RoundingModeDeco == std::numeric_limits<unsigned>::max())
1715Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &
I) {
1719 B.SetInsertPoint(&
I);
1720 SmallVector<Value *, 4>
Args;
1722 Args.push_back(
I.getCondition());
1725 for (
auto &Case :
I.cases()) {
1726 Args.push_back(Case.getCaseValue());
1727 BBCases.
push_back(Case.getCaseSuccessor());
1730 CallInst *NewI =
B.CreateIntrinsic(Intrinsic::spv_switch,
1731 {
I.getOperand(0)->getType()}, {
Args});
1735 I.eraseFromParent();
1738 B.SetInsertPoint(ParentBB);
1739 IndirectBrInst *BrI =
B.CreateIndirectBr(
1742 for (BasicBlock *BBCase : BBCases)
1748 if (
GEP->getNumIndices() == 0)
1751 return CI->getZExtValue() == 0;
1756Instruction *SPIRVEmitIntrinsics::visitIntrinsicInst(IntrinsicInst &
I) {
1762 B.SetInsertPoint(&
I);
1764 SmallVector<Value *, 4>
Args;
1765 Args.push_back(
B.getInt1(
true));
1766 Args.push_back(
I.getOperand(0));
1767 Args.push_back(
B.getInt32(0));
1768 for (
unsigned J = 0; J < SGEP->getNumIndices(); ++J)
1769 Args.push_back(SGEP->getIndexOperand(J));
1771 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, Types, Args);
1772 replaceAllUsesWithAndErase(
B, &
I, NewI);
1776Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &
I) {
1778 B.SetInsertPoint(&
I);
1786 if (
I.getSourceElementType() ==
1787 IntegerType::getInt8Ty(CurrF->
getContext())) {
1788 return buildLogicalAccessChainFromGEP(
I);
1793 Value *PtrOp =
I.getPointerOperand();
1794 Type *SrcElemTy =
I.getSourceElementType();
1795 Type *DeducedPointeeTy = deduceElementType(PtrOp,
true);
1798 if (ArrTy->getElementType() == SrcElemTy) {
1800 Type *FirstIdxType =
I.getOperand(1)->getType();
1801 NewIndices.
push_back(ConstantInt::get(FirstIdxType, 0));
1802 for (
Value *Idx :
I.indices())
1806 SmallVector<Value *, 4>
Args;
1807 Args.push_back(
B.getInt1(
I.isInBounds()));
1808 Args.push_back(
I.getPointerOperand());
1811 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
1812 replaceAllUsesWithAndErase(
B, &
I, NewI);
1819 SmallVector<Value *, 4>
Args;
1820 Args.push_back(
B.getInt1(
I.isInBounds()));
1822 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
1823 replaceAllUsesWithAndErase(
B, &
I, NewI);
1827Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &
I) {
1829 B.SetInsertPoint(&
I);
1838 I.eraseFromParent();
1844 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_bitcast, {
Types}, {
Args});
1845 replaceAllUsesWithAndErase(
B, &
I, NewI);
1849void SPIRVEmitIntrinsics::insertAssignPtrTypeTargetExt(
1851 Type *VTy =
V->getType();
1856 if (ElemTy != AssignedType)
1869 if (CurrentType == AssignedType)
1876 " for value " +
V->getName(),
1884void SPIRVEmitIntrinsics::replacePointerOperandWithPtrCast(
1885 Instruction *
I,
Value *Pointer,
Type *ExpectedElementType,
1887 TypeValidated.insert(
I);
1890 Type *PointerElemTy = deduceElementTypeHelper(Pointer,
false);
1891 if (PointerElemTy == ExpectedElementType ||
1897 MetadataAsValue *VMD =
buildMD(ExpectedElementVal);
1899 bool FirstPtrCastOrAssignPtrType =
true;
1905 for (
auto User :
Pointer->users()) {
1908 (
II->getIntrinsicID() != Intrinsic::spv_assign_ptr_type &&
1909 II->getIntrinsicID() != Intrinsic::spv_ptrcast) ||
1910 II->getOperand(0) != Pointer)
1915 FirstPtrCastOrAssignPtrType =
false;
1916 if (
II->getOperand(1) != VMD ||
1923 if (
II->getIntrinsicID() != Intrinsic::spv_ptrcast)
1928 if (
II->getParent() !=
I->getParent())
1931 I->setOperand(OperandToReplace,
II);
1937 if (FirstPtrCastOrAssignPtrType) {
1942 }
else if (isTodoType(Pointer)) {
1943 eraseTodoType(Pointer);
1951 DenseSet<std::pair<Value *, Value *>> VisitedSubst{
1952 std::make_pair(
I, Pointer)};
1954 propagateElemType(Pointer, PrevElemTy, VisitedSubst);
1966 auto *PtrCastI =
B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
1972void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *
I,
1977 replacePointerOperandWithPtrCast(
1978 I,
SI->getValueOperand(), IntegerType::getInt8Ty(CurrF->
getContext()),
1984 Type *OpTy =
Op->getType();
1987 if (OpTy ==
Op->getType())
1988 OpTy = deduceElementTypeByValueDeep(OpTy,
Op,
false);
1989 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 1,
B);
1994 Type *OpTy = LI->getType();
1999 Type *NewOpTy = OpTy;
2000 OpTy = deduceElementTypeByValueDeep(OpTy, LI,
false);
2001 if (OpTy == NewOpTy)
2002 insertTodoType(Pointer);
2005 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 0,
B);
2010 Type *OpTy =
nullptr;
2022 OpTy = GEPI->getSourceElementType();
2024 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 0,
B);
2026 insertTodoType(Pointer);
2038 std::string DemangledName =
2042 bool HaveTypes =
false;
2060 for (User *U : CalledArg->
users()) {
2062 if ((ElemTy = deduceElementTypeHelper(Inst,
false)) !=
nullptr)
2068 HaveTypes |= ElemTy !=
nullptr;
2073 if (DemangledName.empty() && !HaveTypes)
2091 Type *ExpectedType =
2093 if (!ExpectedType && !DemangledName.empty())
2094 ExpectedType = SPIRV::parseBuiltinCallArgumentBaseType(
2095 DemangledName,
OpIdx,
I->getContext());
2096 if (!ExpectedType || ExpectedType->
isVoidTy())
2104 replacePointerOperandWithPtrCast(CI, ArgOperand, ExpectedType,
OpIdx,
B);
2108Instruction *SPIRVEmitIntrinsics::visitInsertElementInst(InsertElementInst &
I) {
2115 I.getOperand(1)->getType(),
2116 I.getOperand(2)->getType()};
2118 B.SetInsertPoint(&
I);
2120 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_insertelt, {
Types}, {
Args});
2121 replaceAllUsesWithAndErase(
B, &
I, NewI);
2126SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &
I) {
2133 B.SetInsertPoint(&
I);
2135 I.getIndexOperand()->getType()};
2136 SmallVector<Value *, 2>
Args = {
I.getVectorOperand(),
I.getIndexOperand()};
2137 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_extractelt, {
Types}, {
Args});
2138 replaceAllUsesWithAndErase(
B, &
I, NewI);
2142Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &
I) {
2144 B.SetInsertPoint(&
I);
2147 Value *AggregateOp =
I.getAggregateOperand();
2151 Args.push_back(AggregateOp);
2152 Args.push_back(
I.getInsertedValueOperand());
2153 for (
auto &
Op :
I.indices())
2154 Args.push_back(
B.getInt32(
Op));
2156 B.CreateIntrinsic(Intrinsic::spv_insertv, {
Types}, {
Args});
2157 replaceMemInstrUses(&
I, NewI,
B);
2161Instruction *SPIRVEmitIntrinsics::visitExtractValueInst(ExtractValueInst &
I) {
2162 if (
I.getAggregateOperand()->getType()->isAggregateType())
2165 B.SetInsertPoint(&
I);
2167 for (
auto &
Op :
I.indices())
2168 Args.push_back(
B.getInt32(
Op));
2170 B.CreateIntrinsic(Intrinsic::spv_extractv, {
I.getType()}, {
Args});
2171 replaceAllUsesWithAndErase(
B, &
I, NewI);
2175Instruction *SPIRVEmitIntrinsics::visitLoadInst(LoadInst &
I) {
2176 if (!
I.getType()->isAggregateType())
2179 B.SetInsertPoint(&
I);
2180 TrackConstants =
false;
2185 B.CreateIntrinsic(Intrinsic::spv_load, {
I.getOperand(0)->getType()},
2186 {
I.getPointerOperand(),
B.getInt16(Flags),
2187 B.getInt32(
I.getAlign().value())});
2188 replaceMemInstrUses(&
I, NewI,
B);
2192Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &
I) {
2196 B.SetInsertPoint(&
I);
2197 TrackConstants =
false;
2201 auto *PtrOp =
I.getPointerOperand();
2203 if (
I.getValueOperand()->getType()->isAggregateType()) {
2211 "Unexpected argument of aggregate type, should be spv_extractv!");
2215 auto *NewI =
B.CreateIntrinsic(
2216 Intrinsic::spv_store, {
I.getValueOperand()->getType(), PtrOp->
getType()},
2217 {
I.getValueOperand(), PtrOp,
B.getInt16(Flags),
2218 B.getInt32(
I.getAlign().value())});
2220 I.eraseFromParent();
2224Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &
I) {
2225 Value *ArraySize =
nullptr;
2226 if (
I.isArrayAllocation()) {
2229 SPIRV::Extension::SPV_INTEL_variable_length_array))
2231 "array allocation: this instruction requires the following "
2232 "SPIR-V extension: SPV_INTEL_variable_length_array",
2234 ArraySize =
I.getArraySize();
2237 B.SetInsertPoint(&
I);
2238 TrackConstants =
false;
2239 Type *PtrTy =
I.getType();
2242 ?
B.CreateIntrinsic(Intrinsic::spv_alloca_array,
2243 {PtrTy, ArraySize->
getType()},
2244 {ArraySize,
B.getInt32(
I.getAlign().value())})
2245 :
B.CreateIntrinsic(
Intrinsic::spv_alloca, {PtrTy},
2246 {
B.getInt32(
I.getAlign().value())});
2247 replaceAllUsesWithAndErase(
B, &
I, NewI);
2251Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &
I) {
2252 assert(
I.getType()->isAggregateType() &&
"Aggregate result is expected");
2254 B.SetInsertPoint(&
I);
2256 Args.push_back(
B.getInt32(
2257 static_cast<uint32_t
>(
getMemScope(
I.getContext(),
I.getSyncScopeID()))));
2258 Args.push_back(
B.getInt32(
2260 Args.push_back(
B.getInt32(
2262 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_cmpxchg,
2263 {
I.getPointerOperand()->getType()}, {
Args});
2264 replaceMemInstrUses(&
I, NewI,
B);
2268Instruction *SPIRVEmitIntrinsics::visitUnreachableInst(UnreachableInst &
I) {
2270 B.SetInsertPoint(&
I);
2271 B.CreateIntrinsic(Intrinsic::spv_unreachable, {});
2280 static const StringSet<> ArtificialGlobals{
"llvm.global.annotations",
2281 "llvm.compiler.used",
"llvm.used"};
2286 auto &UserFunctions = GVUsers.getTransitiveUserFunctions(GV);
2287 if (UserFunctions.contains(
F))
2292 if (!UserFunctions.empty())
2297 const Module &M = *
F->getParent();
2298 const Function &FirstDefinition = *M.getFunctionDefs().
begin();
2299 return F == &FirstDefinition;
2302Value *SPIRVEmitIntrinsics::buildSpvUndefComposite(
Type *AggrTy,
2304 SmallVector<Value *, 4> Elems;
2306 Type *ElemTy = ArrTy->getElementType();
2307 auto *UI =
B.CreateIntrinsic(Intrinsic::spv_undef, {});
2309 AggrConstTypes[UI] = ElemTy;
2310 Elems.
assign(ArrTy->getNumElements(), UI);
2313 DenseMap<Type *, Instruction *> UndefByType;
2314 for (
unsigned I = 0;
I < StructTy->getNumElements(); ++
I) {
2316 auto &
Entry = UndefByType[ElemTy];
2318 Entry =
B.CreateIntrinsic(Intrinsic::spv_undef, {});
2320 AggrConstTypes[
Entry] = ElemTy;
2325 auto *Composite =
B.CreateIntrinsic(Intrinsic::spv_const_composite,
2326 {
B.getInt32Ty()}, Elems);
2328 AggrConstTypes[Composite] = AggrTy;
2332void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV,
2343 deduceElementTypeHelper(&GV,
false);
2345 Value *InitOp = Init;
2347 InitOp = buildSpvUndefComposite(Init->
getType(),
B);
2350 auto *InitInst =
B.CreateIntrinsic(Intrinsic::spv_init_global,
2352 InitInst->setArgOperand(1, InitOp);
2355 B.CreateIntrinsic(Intrinsic::spv_unref_global, GV.
getType(), &GV);
2361bool SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *
I,
2363 bool UnknownElemTypeI8) {
2369 if (
Type *ElemTy = deduceElementType(
I, UnknownElemTypeI8)) {
2376void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *
I,
2379 static StringMap<unsigned> ResTypeWellKnown = {
2380 {
"async_work_group_copy", WellKnownTypes::Event},
2381 {
"async_work_group_strided_copy", WellKnownTypes::Event},
2382 {
"__spirv_GroupAsyncCopy", WellKnownTypes::Event}};
2386 bool IsKnown =
false;
2391 std::string DemangledName =
2394 if (DemangledName.length() > 0)
2396 SPIRV::lookupBuiltinNameHelper(DemangledName, &DecorationId);
2397 auto ResIt = ResTypeWellKnown.
find(DemangledName);
2398 if (ResIt != ResTypeWellKnown.
end()) {
2401 switch (ResIt->second) {
2402 case WellKnownTypes::Event:
2409 switch (DecorationId) {
2412 case FPDecorationId::SAT:
2415 case FPDecorationId::RTE:
2417 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTE,
B);
2419 case FPDecorationId::RTZ:
2421 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTZ,
B);
2423 case FPDecorationId::RTP:
2425 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTP,
B);
2427 case FPDecorationId::RTN:
2429 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTN,
B);
2435 Type *Ty =
I->getType();
2438 Type *TypeToAssign = Ty;
2440 if (
II->getIntrinsicID() == Intrinsic::spv_const_composite ||
2441 II->getIntrinsicID() == Intrinsic::spv_undef) {
2442 auto It = AggrConstTypes.
find(
II);
2443 if (It == AggrConstTypes.
end())
2445 TypeToAssign = It->second;
2447 }
else if (
auto It = AggrConstTypes.
find(
I); It != AggrConstTypes.
end())
2448 TypeToAssign = It->second;
2452 for (
const auto &
Op :
I->operands()) {
2459 Type *OpTy =
Op->getType();
2461 CallInst *AssignCI =
2466 Type *OpTy =
Op->getType();
2481 CallInst *AssignCI =
2491bool SPIRVEmitIntrinsics::shouldTryToAddMemAliasingDecoration(
2492 Instruction *Inst) {
2494 if (!STI->
canUseExtension(SPIRV::Extension::SPV_INTEL_memory_access_aliasing))
2505 case Intrinsic::spv_load:
2506 case Intrinsic::spv_store:
2513 const std::string
Prefix =
"__spirv_Atomic";
2514 const bool IsAtomic =
Name.find(Prefix) == 0;
2522void SPIRVEmitIntrinsics::insertSpirvDecorations(Instruction *
I,
2524 if (MDNode *MD =
I->getMetadata(
"spirv.Decorations")) {
2526 B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {
I->getType()},
2531 auto processMemAliasingDecoration = [&](
unsigned Kind) {
2532 if (MDNode *AliasListMD =
I->getMetadata(Kind)) {
2533 if (shouldTryToAddMemAliasingDecoration(
I)) {
2534 uint32_t Dec =
Kind == LLVMContext::MD_alias_scope
2535 ? SPIRV::Decoration::AliasScopeINTEL
2536 : SPIRV::Decoration::NoAliasINTEL;
2538 I, ConstantInt::get(
B.getInt32Ty(), Dec),
2541 B.CreateIntrinsic(Intrinsic::spv_assign_aliasing_decoration,
2542 {
I->getType()}, {
Args});
2546 processMemAliasingDecoration(LLVMContext::MD_alias_scope);
2547 processMemAliasingDecoration(LLVMContext::MD_noalias);
2550 if (MDNode *MD =
I->getMetadata(LLVMContext::MD_fpmath)) {
2552 bool AllowFPMaxError =
2554 if (!AllowFPMaxError)
2558 B.CreateIntrinsic(Intrinsic::spv_assign_fpmaxerror_decoration,
2567 &FPFastMathDefaultInfoMap,
2569 auto it = FPFastMathDefaultInfoMap.
find(
F);
2570 if (it != FPFastMathDefaultInfoMap.
end())
2578 SPIRV::FPFastMathMode::None);
2580 SPIRV::FPFastMathMode::None);
2582 SPIRV::FPFastMathMode::None);
2583 return FPFastMathDefaultInfoMap[
F] = std::move(FPFastMathDefaultInfoVec);
2589 size_t BitWidth = Ty->getScalarSizeInBits();
2593 assert(Index >= 0 && Index < 3 &&
2594 "Expected FPFastMathDefaultInfo for half, float, or double");
2595 assert(FPFastMathDefaultInfoVec.
size() == 3 &&
2596 "Expected FPFastMathDefaultInfoVec to have exactly 3 elements");
2597 return FPFastMathDefaultInfoVec[Index];
2600void SPIRVEmitIntrinsics::insertConstantsForFPFastMathDefault(
Module &M) {
2602 if (!
ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2))
2611 auto Node =
M.getNamedMetadata(
"spirv.ExecutionMode");
2613 if (!
M.getNamedMetadata(
"opencl.enable.FP_CONTRACT")) {
2621 ConstantInt::get(Type::getInt32Ty(
M.getContext()), 0);
2624 [[maybe_unused]] GlobalVariable *GV =
2625 new GlobalVariable(M,
2626 Type::getInt32Ty(
M.getContext()),
2640 DenseMap<Function *, SPIRV::FPFastMathDefaultInfoVector>
2641 FPFastMathDefaultInfoMap;
2643 for (
unsigned i = 0; i <
Node->getNumOperands(); i++) {
2652 if (EM == SPIRV::ExecutionMode::FPFastMathDefault) {
2654 "Expected 4 operands for FPFastMathDefault");
2660 SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec =
2662 SPIRV::FPFastMathDefaultInfo &
Info =
2665 Info.FPFastMathDefault =
true;
2666 }
else if (EM == SPIRV::ExecutionMode::ContractionOff) {
2668 "Expected no operands for ContractionOff");
2672 SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec =
2674 for (SPIRV::FPFastMathDefaultInfo &Info : FPFastMathDefaultInfoVec) {
2675 Info.ContractionOff =
true;
2677 }
else if (EM == SPIRV::ExecutionMode::SignedZeroInfNanPreserve) {
2679 "Expected 1 operand for SignedZeroInfNanPreserve");
2680 unsigned TargetWidth =
2685 SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec =
2689 assert(Index >= 0 && Index < 3 &&
2690 "Expected FPFastMathDefaultInfo for half, float, or double");
2691 assert(FPFastMathDefaultInfoVec.
size() == 3 &&
2692 "Expected FPFastMathDefaultInfoVec to have exactly 3 elements");
2693 FPFastMathDefaultInfoVec[
Index].SignedZeroInfNanPreserve =
true;
2697 std::unordered_map<unsigned, GlobalVariable *> GlobalVars;
2698 for (
auto &[Func, FPFastMathDefaultInfoVec] : FPFastMathDefaultInfoMap) {
2699 if (FPFastMathDefaultInfoVec.
empty())
2702 for (
const SPIRV::FPFastMathDefaultInfo &Info : FPFastMathDefaultInfoVec) {
2703 assert(
Info.Ty &&
"Expected target type for FPFastMathDefaultInfo");
2706 if (Flags == SPIRV::FPFastMathMode::None && !
Info.ContractionOff &&
2707 !
Info.SignedZeroInfNanPreserve && !
Info.FPFastMathDefault)
2711 if (
Info.ContractionOff && (Flags & SPIRV::FPFastMathMode::AllowContract))
2713 "and AllowContract");
2715 if (
Info.SignedZeroInfNanPreserve &&
2717 (SPIRV::FPFastMathMode::NotNaN | SPIRV::FPFastMathMode::NotInf |
2718 SPIRV::FPFastMathMode::NSZ))) {
2719 if (
Info.FPFastMathDefault)
2721 "SignedZeroInfNanPreserve but at least one of "
2722 "NotNaN/NotInf/NSZ is enabled.");
2725 if ((Flags & SPIRV::FPFastMathMode::AllowTransform) &&
2726 !((Flags & SPIRV::FPFastMathMode::AllowReassoc) &&
2727 (Flags & SPIRV::FPFastMathMode::AllowContract))) {
2729 "AllowTransform requires AllowReassoc and "
2730 "AllowContract to be set.");
2733 auto it = GlobalVars.find(Flags);
2734 GlobalVariable *GV =
nullptr;
2735 if (it != GlobalVars.end()) {
2741 ConstantInt::get(Type::getInt32Ty(
M.getContext()), Flags);
2744 GV =
new GlobalVariable(M,
2745 Type::getInt32Ty(
M.getContext()),
2750 GlobalVars[
Flags] = GV;
2756void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *
I,
2759 bool IsConstComposite =
2760 II &&
II->getIntrinsicID() == Intrinsic::spv_const_composite;
2761 if (IsConstComposite && TrackConstants) {
2763 auto t = AggrConsts.
find(
I);
2767 {
II->getType(),
II->getType()}, t->second,
I, {},
B);
2769 NewOp->setArgOperand(0,
I);
2772 for (
const auto &
Op :
I->operands()) {
2776 unsigned OpNo =
Op.getOperandNo();
2777 if (
II && ((
II->getIntrinsicID() == Intrinsic::spv_gep && OpNo == 0) ||
2778 (!
II->isBundleOperand(OpNo) &&
2779 II->paramHasAttr(OpNo, Attribute::ImmArg))))
2783 IsPhi ?
B.SetInsertPointPastAllocas(
I->getParent()->getParent())
2784 :
B.SetInsertPoint(
I);
2787 Type *OpTy =
Op->getType();
2795 {OpTy, OpTyVal->
getType()},
Op, OpTyVal, {},
B);
2797 if (!IsConstComposite &&
isPointerTy(OpTy) && OpElemTy !=
nullptr &&
2798 OpElemTy != IntegerType::getInt8Ty(
I->getContext())) {
2800 SmallVector<Value *, 2>
Args = {
2803 CallInst *PtrCasted =
2804 B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
2809 I->setOperand(OpNo, NewOp);
2811 if (Named.insert(
I).second)
2815Type *SPIRVEmitIntrinsics::deduceFunParamElementType(Function *
F,
2817 std::unordered_set<Function *> FVisited;
2818 return deduceFunParamElementType(
F,
OpIdx, FVisited);
2821Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
2822 Function *
F,
unsigned OpIdx, std::unordered_set<Function *> &FVisited) {
2824 if (!FVisited.insert(
F).second)
2827 std::unordered_set<Value *> Visited;
2830 for (User *U :
F->users()) {
2842 if (
Type *Ty = deduceElementTypeHelper(OpArg, Visited,
false))
2845 for (User *OpU : OpArg->
users()) {
2847 if (!Inst || Inst == CI)
2850 if (
Type *Ty = deduceElementTypeHelper(Inst, Visited,
false))
2857 if (FVisited.find(OuterF) != FVisited.end())
2859 for (
unsigned i = 0; i < OuterF->
arg_size(); ++i) {
2860 if (OuterF->
getArg(i) == OpArg) {
2861 Lookup.push_back(std::make_pair(OuterF, i));
2868 for (
auto &Pair :
Lookup) {
2869 if (
Type *Ty = deduceFunParamElementType(Pair.first, Pair.second, FVisited))
2876void SPIRVEmitIntrinsics::processParamTypesByFunHeader(Function *
F,
2878 B.SetInsertPointPastAllocas(
F);
2892 for (User *U :
F->users()) {
2908 for (User *U : Arg->
users()) {
2912 CI->
getParent()->getParent() == CurrF) {
2914 deduceOperandElementTypeFunctionPointer(CI,
Ops, ElemTy,
false);
2925void SPIRVEmitIntrinsics::processParamTypes(Function *
F,
IRBuilder<> &
B) {
2926 B.SetInsertPointPastAllocas(
F);
2932 if (!ElemTy && (ElemTy = deduceFunParamElementType(
F,
OpIdx)) !=
nullptr) {
2934 DenseSet<std::pair<Value *, Value *>> VisitedSubst;
2936 propagateElemType(Arg, IntegerType::getInt8Ty(
F->getContext()),
2948 bool IsNewFTy =
false;
2964bool SPIRVEmitIntrinsics::processFunctionPointers(
Module &M) {
2967 if (
F.isIntrinsic())
2969 if (
F.isDeclaration()) {
2970 for (User *U :
F.users()) {
2983 for (User *U :
F.users()) {
2985 if (!
II ||
II->arg_size() != 3 ||
II->getOperand(0) != &
F)
2987 if (
II->getIntrinsicID() == Intrinsic::spv_assign_ptr_type ||
2988 II->getIntrinsicID() == Intrinsic::spv_ptrcast) {
2995 if (Worklist.
empty())
2998 LLVMContext &Ctx =
M.getContext();
3003 for (Function *
F : Worklist) {
3005 for (
const auto &Arg :
F->args())
3007 IRB.CreateCall(
F, Args);
3009 IRB.CreateRetVoid();
3015void SPIRVEmitIntrinsics::applyDemangledPtrArgTypes(
IRBuilder<> &
B) {
3016 DenseMap<Function *, CallInst *> Ptrcasts;
3017 for (
auto It : FDeclPtrTys) {
3019 for (
auto *U :
F->users()) {
3024 for (
auto [Idx, ElemTy] : It.second) {
3032 B.SetInsertPointPastAllocas(Arg->
getParent());
3036 }
else if (isaGEP(Param)) {
3037 replaceUsesOfWithSpvPtrcast(Param,
normalizeType(ElemTy), CI,
3046 .getFirstNonPHIOrDbgOrAlloca());
3067SPIRVEmitIntrinsics::simplifyZeroLengthArrayGepInst(GetElementPtrInst *
GEP) {
3074 Type *SrcTy =
GEP->getSourceElementType();
3075 SmallVector<Value *, 8> Indices(
GEP->indices());
3077 if (ArrTy && ArrTy->getNumElements() == 0 &&
3079 Indices.erase(Indices.begin());
3080 SrcTy = ArrTy->getElementType();
3082 GEP->getNoWrapFlags(),
"",
3083 GEP->getIterator());
3088void SPIRVEmitIntrinsics::emitUnstructuredLoopControls(Function &
F,
3095 if (
ST->canUseExtension(
3096 SPIRV::Extension::SPV_INTEL_unstructured_loop_controls)) {
3097 for (BasicBlock &BB :
F) {
3099 MDNode *LoopMD =
Term->getMetadata(LLVMContext::MD_loop);
3105 unsigned LC =
Ops[0];
3106 if (LC == SPIRV::LoopControl::None)
3110 B.SetInsertPoint(Term);
3111 SmallVector<Value *, 4> IntrArgs;
3112 for (
unsigned Op :
Ops)
3114 B.CreateIntrinsic(Intrinsic::spv_loop_control_intel, IntrArgs);
3121 DominatorTree DT(
F);
3126 for (Loop *L : LI.getLoopsInPreorder()) {
3137 if (LoopControlOps[0] == SPIRV::LoopControl::None)
3141 B.SetInsertPoint(Header->getTerminator());
3144 SmallVector<Value *, 4>
Args = {MergeAddress, ContinueAddress};
3145 for (
unsigned Imm : LoopControlOps)
3146 Args.emplace_back(
B.getInt32(Imm));
3147 B.CreateIntrinsic(Intrinsic::spv_loop_merge, {
Args});
3151bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
3152 if (
Func.isDeclaration())
3156 GR =
ST.getSPIRVGlobalRegistry();
3160 ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
3165 AggrConstTypes.
clear();
3167 DeletedInstrs.
clear();
3171 SmallPtrSet<Instruction *, 4> DeadInsts;
3176 if ((!
GEP && !SGEP) || GR->findDeducedElementType(&
I))
3180 GR->addDeducedElementType(SGEP,
3185 GetElementPtrInst *NewGEP = simplifyZeroLengthArrayGepInst(
GEP);
3187 GEP->replaceAllUsesWith(NewGEP);
3191 if (
Type *GepTy = getGEPType(
GEP))
3195 for (
auto *
I : DeadInsts) {
3196 assert(
I->use_empty() &&
"Dead instruction should not have any uses left");
3197 I->eraseFromParent();
3200 processParamTypesByFunHeader(CurrF,
B);
3209 Type *ElTy =
SI->getValueOperand()->getType();
3214 B.SetInsertPoint(&
Func.getEntryBlock(),
Func.getEntryBlock().begin());
3215 for (
auto &GV :
Func.getParent()->globals())
3216 processGlobalValue(GV,
B);
3218 preprocessUndefs(
B);
3219 preprocessCompositeConstants(
B);
3221 for (BasicBlock &BB : Func)
3222 for (PHINode &Phi : BB.
phis())
3223 if (
Phi.getType()->isAggregateType()) {
3224 AggrConstTypes[&
Phi] =
Phi.getType();
3225 Phi.mutateType(
B.getInt32Ty());
3228 preprocessBoolVectorBitcasts(Func);
3232 applyDemangledPtrArgTypes(
B);
3235 for (
auto &
I : Worklist) {
3237 if (isConvergenceIntrinsic(
I))
3240 bool Postpone = insertAssignPtrTypeIntrs(
I,
B,
false);
3242 insertAssignTypeIntrs(
I,
B);
3243 insertPtrCastOrAssignTypeInstr(
I,
B);
3247 if (Postpone && !GR->findAssignPtrTypeInstr(
I))
3248 insertAssignPtrTypeIntrs(
I,
B,
true);
3251 useRoundingMode(FPI,
B);
3256 SmallPtrSet<Instruction *, 4> IncompleteRets;
3258 deduceOperandElementType(&
I, &IncompleteRets);
3262 for (BasicBlock &BB : Func)
3263 for (PHINode &Phi : BB.
phis())
3265 deduceOperandElementType(&Phi,
nullptr);
3267 for (
auto *
I : Worklist) {
3268 if (DeletedInstrs.
count(
I))
3270 TrackConstants =
true;
3280 if (isConvergenceIntrinsic(
I))
3284 processInstrAfterVisit(
I,
B);
3287 emitUnstructuredLoopControls(Func,
B);
3293bool SPIRVEmitIntrinsics::postprocessTypes(
Module &M) {
3294 if (!GR || TodoTypeSz == 0)
3297 unsigned SzTodo = TodoTypeSz;
3298 DenseMap<Value *, SmallPtrSet<Value *, 4>> ToProcess;
3303 CallInst *AssignCI = GR->findAssignPtrTypeInstr(
Op);
3304 Type *KnownTy = GR->findDeducedElementType(
Op);
3305 if (!KnownTy || !AssignCI)
3311 std::unordered_set<Value *> Visited;
3312 if (
Type *ElemTy = deduceElementTypeHelper(
Op, Visited,
false,
true)) {
3313 if (ElemTy != KnownTy) {
3314 DenseSet<std::pair<Value *, Value *>> VisitedSubst;
3315 propagateElemType(CI, ElemTy, VisitedSubst);
3322 if (
Op->hasUseList()) {
3323 for (User *U :
Op->users()) {
3330 if (TodoTypeSz == 0)
3335 SmallPtrSet<Instruction *, 4> IncompleteRets;
3337 auto It = ToProcess.
find(&
I);
3338 if (It == ToProcess.
end())
3340 It->second.remove_if([
this](
Value *V) {
return !isTodoType(V); });
3341 if (It->second.size() == 0)
3343 deduceOperandElementType(&
I, &IncompleteRets, &It->second,
true);
3344 if (TodoTypeSz == 0)
3349 return SzTodo > TodoTypeSz;
3353void SPIRVEmitIntrinsics::parseFunDeclarations(
Module &M) {
3355 if (!
F.isDeclaration() ||
F.isIntrinsic())
3359 if (DemangledName.empty())
3363 auto [Grp, Opcode, ExtNo] = SPIRV::mapBuiltinToOpcode(
3364 DemangledName,
ST.getPreferredInstructionSet());
3365 if (Opcode != SPIRV::OpGroupAsyncCopy)
3368 SmallVector<unsigned> Idxs;
3377 LLVMContext &Ctx =
F.getContext();
3379 SPIRV::parseBuiltinTypeStr(TypeStrs, DemangledName, Ctx);
3380 if (!TypeStrs.
size())
3383 for (
unsigned Idx : Idxs) {
3384 if (Idx >= TypeStrs.
size())
3387 SPIRV::parseBuiltinCallArgumentType(TypeStrs[Idx].trim(), Ctx))
3390 FDeclPtrTys[&
F].push_back(std::make_pair(Idx, ElemTy));
3395bool SPIRVEmitIntrinsics::processMaskedMemIntrinsic(IntrinsicInst &
I) {
3396 const SPIRVSubtarget &
ST = TM.
getSubtarget<SPIRVSubtarget>(*
I.getFunction());
3398 if (
I.getIntrinsicID() == Intrinsic::masked_gather) {
3399 if (!
ST.canUseExtension(
3400 SPIRV::Extension::SPV_INTEL_masked_gather_scatter)) {
3401 I.getContext().emitError(
3402 &
I,
"llvm.masked.gather requires SPV_INTEL_masked_gather_scatter "
3406 I.eraseFromParent();
3412 Value *Ptrs =
I.getArgOperand(0);
3414 Value *Passthru =
I.getArgOperand(2);
3417 uint32_t Alignment =
I.getParamAlign(0).valueOrOne().value();
3419 SmallVector<Value *, 4>
Args = {Ptrs,
B.getInt32(Alignment),
Mask,
3424 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_masked_gather, Types, Args);
3426 I.eraseFromParent();
3430 if (
I.getIntrinsicID() == Intrinsic::masked_scatter) {
3431 if (!
ST.canUseExtension(
3432 SPIRV::Extension::SPV_INTEL_masked_gather_scatter)) {
3433 I.getContext().emitError(
3434 &
I,
"llvm.masked.scatter requires SPV_INTEL_masked_gather_scatter "
3437 I.eraseFromParent();
3443 Value *Values =
I.getArgOperand(0);
3444 Value *Ptrs =
I.getArgOperand(1);
3449 uint32_t Alignment =
I.getParamAlign(1).valueOrOne().value();
3451 SmallVector<Value *, 4>
Args = {Values, Ptrs,
B.getInt32(Alignment),
Mask};
3455 B.CreateIntrinsic(Intrinsic::spv_masked_scatter, Types, Args);
3456 I.eraseFromParent();
3467void SPIRVEmitIntrinsics::preprocessBoolVectorBitcasts(Function &
F) {
3468 struct BoolVecBitcast {
3470 FixedVectorType *BoolVecTy;
3474 auto getAsBoolVec = [](
Type *Ty) -> FixedVectorType * {
3476 return (VTy && VTy->getElementType()->
isIntegerTy(1)) ? VTy :
nullptr;
3484 if (
auto *BVTy = getAsBoolVec(BC->getSrcTy()))
3486 else if (
auto *BVTy = getAsBoolVec(BC->getDestTy()))
3490 for (
auto &[BC, BoolVecTy, SrcIsBoolVec] : ToReplace) {
3492 Value *Src = BC->getOperand(0);
3493 unsigned BoolVecN = BoolVecTy->getNumElements();
3495 Type *IntTy =
B.getIntNTy(BoolVecN);
3501 IntVal = ConstantInt::get(IntTy, 0);
3502 for (
unsigned I = 0;
I < BoolVecN; ++
I) {
3503 Value *Elem =
B.CreateExtractElement(Src,
B.getInt32(
I));
3504 Value *Ext =
B.CreateZExt(Elem, IntTy);
3506 Ext =
B.CreateShl(Ext, ConstantInt::get(IntTy,
I));
3507 IntVal =
B.CreateOr(IntVal, Ext);
3513 if (!Src->getType()->isIntegerTy())
3514 IntVal =
B.CreateBitCast(Src, IntTy);
3519 if (!SrcIsBoolVec) {
3522 for (
unsigned I = 0;
I < BoolVecN; ++
I) {
3525 Value *
Cmp =
B.CreateICmpNE(
And, ConstantInt::get(IntTy, 0));
3526 Result =
B.CreateInsertElement(Result, Cmp,
B.getInt32(
I));
3532 if (!BC->getDestTy()->isIntegerTy())
3533 Result =
B.CreateBitCast(IntVal, BC->getDestTy());
3536 BC->replaceAllUsesWith(Result);
3537 BC->eraseFromParent();
3541bool SPIRVEmitIntrinsics::convertMaskedMemIntrinsics(
Module &M) {
3545 if (!
F.isIntrinsic())
3548 if (IID != Intrinsic::masked_gather && IID != Intrinsic::masked_scatter)
3553 Changed |= processMaskedMemIntrinsic(*
II);
3557 F.eraseFromParent();
3563bool SPIRVEmitIntrinsics::runOnModule(
Module &M) {
3566 Changed |= convertMaskedMemIntrinsics(M);
3568 parseFunDeclarations(M);
3569 insertConstantsForFPFastMathDefault(M);
3580 if (!
F.isDeclaration() && !
F.isIntrinsic()) {
3582 processParamTypes(&
F,
B);
3586 CanTodoType =
false;
3587 Changed |= postprocessTypes(M);
3590 Changed |= processFunctionPointers(M);
3597 SPIRVEmitIntrinsics Legacy(TM);
3598 if (Legacy.runOnModule(M))
3604 return new SPIRVEmitIntrinsics(TM);
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static void replaceAllUsesWith(Value *Old, Value *New, SmallPtrSet< BasicBlock *, 32 > &FreshBBs, bool IsHuge)
Replace all old uses with new ones, and push the updated BBs into FreshBBs.
static Type * getPointeeType(Value *Ptr, const DataLayout &DL)
This file defines the DenseSet and SmallDenseSet classes.
static bool runOnFunction(Function &F, bool PostInlining)
iv Induction Variable Users
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
Machine Check Debug Module
MachineInstr unsigned OpIdx
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
static unsigned getNumElements(Type *Ty)
static bool isMemInstrToReplace(Instruction *I)
static bool isAggrConstForceInt32(const Value *V)
static SPIRV::FPFastMathDefaultInfoVector & getOrCreateFPFastMathDefaultInfoVec(const Module &M, DenseMap< Function *, SPIRV::FPFastMathDefaultInfoVector > &FPFastMathDefaultInfoMap, Function *F)
static Type * getAtomicElemTy(SPIRVGlobalRegistry *GR, Instruction *I, Value *PointerOperand)
static void reportFatalOnTokenType(const Instruction *I)
static void setInsertPointAfterDef(IRBuilder<> &B, Instruction *I)
static void emitAssignName(Instruction *I, IRBuilder<> &B)
static Type * getPointeeTypeByCallInst(StringRef DemangledName, Function *CalledF, unsigned OpIdx)
static void createRoundingModeDecoration(Instruction *I, unsigned RoundingModeDeco, IRBuilder<> &B)
static void createDecorationIntrinsic(Instruction *I, MDNode *Node, IRBuilder<> &B)
static SPIRV::FPFastMathDefaultInfo & getFPFastMathDefaultInfo(SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec, const Type *Ty)
static cl::opt< bool > SpirvEmitOpNames("spirv-emit-op-names", cl::desc("Emit OpName for all instructions"), cl::init(false))
static bool IsKernelArgInt8(Function *F, StoreInst *SI)
static void addSaturatedDecorationToIntrinsic(Instruction *I, IRBuilder<> &B)
static bool isFirstIndexZero(const GetElementPtrInst *GEP)
static void setInsertPointSkippingPhis(IRBuilder<> &B, Instruction *I)
static FunctionType * getFunctionPointerElemType(Function *F, SPIRVGlobalRegistry *GR)
static void createSaturatedConversionDecoration(Instruction *I, IRBuilder<> &B)
static bool shouldEmitIntrinsicsForGlobalValue(const GlobalVariableUsers &GVUsers, const GlobalVariable &GV, const Function *F)
static Type * restoreMutatedType(SPIRVGlobalRegistry *GR, Instruction *I, Type *Ty)
static bool requireAssignType(Instruction *I)
static void insertSpirvDecorations(MachineFunction &MF, SPIRVGlobalRegistry *GR, MachineIRBuilder MIB)
static void visit(BasicBlock &Start, std::function< bool(BasicBlock *)> op)
StringSet - A set-like wrapper for the StringMap.
static SymbolRef::Type getType(const Symbol *Sym)
LocallyHashedType DenseMapInfo< LocallyHashedType >::Empty
static std::optional< unsigned > getOpcode(ArrayRef< VPValue * > Values)
Returns the opcode of Values or ~0 if they do not all agree.
static int Lookup(ArrayRef< TableEntry > Table, unsigned Opcode)
static APInt getOneBitSet(unsigned numBits, unsigned BitNo)
Return an APInt with exactly one bit set in the result.
This class represents an incoming formal argument to a Function.
const Function * getParent() const
static unsigned getPointerOperandIndex()
static unsigned getPointerOperandIndex()
iterator_range< const_phi_iterator > phis() const
Returns a range that iterates over the phis in the basic block.
const Function * getParent() const
Return the enclosing method, or null if none.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
LLVM_ABI LLVMContext & getContext() const
Get the context in which this basic block lives.
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
static LLVM_ABI BlockAddress * get(Function *F, BasicBlock *BB)
Return a BlockAddress for the specified function and basic block.
bool isInlineAsm() const
Check if this call is an inline asm statement.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
LLVM_ABI bool isIndirectCall() const
Return true if the callsite is an indirect call.
Value * getCalledOperand() const
Value * getArgOperand(unsigned i) const
LLVM_ABI Intrinsic::ID getIntrinsicID() const
Returns the intrinsic ID of the intrinsic called or Intrinsic::not_intrinsic if the called function i...
iterator_range< User::op_iterator > args()
Iteration adapter for range-for loops.
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
LLVM_ABI std::optional< RoundingMode > getRoundingMode() const
iterator find(const_arg_type_t< KeyT > Val)
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
const DataLayout & getDataLayout() const
Get the data layout of the module this function belongs to.
Intrinsic::ID getIntrinsicID() const LLVM_READONLY
getIntrinsicID - This method returns the ID number of the specified function, or Intrinsic::not_intri...
bool isIntrinsic() const
isIntrinsic - Returns true if the function's name starts with "llvm.".
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Type * getReturnType() const
Returns the type of the ret val.
Argument * getArg(unsigned i) const
an instruction for type-safe pointer arithmetic to access elements of arrays and structs
static LLVM_ABI Type * getTypeAtIndex(Type *Ty, Value *Idx)
Return the type of the element at the given index of an indexable type.
static GetElementPtrInst * Create(Type *PointeeType, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
static unsigned getPointerOperandIndex()
PointerType * getType() const
Global values are always pointers.
@ InternalLinkage
Rename collisions when linking (static functions).
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
LLVM_ABI void addDestination(BasicBlock *Dest)
Add a destination.
Base class for instruction visitors.
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
Instruction * user_back()
Specialize the methods defined in Value, as we know that an instruction can only be used by other ins...
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
LLVM_ABI void copyMetadata(const Instruction &SrcInst, ArrayRef< unsigned > WL=ArrayRef< unsigned >())
Copy metadata from SrcInst to this instruction.
This is an important class for using LLVM in a threaded context.
static unsigned getPointerOperandIndex()
const MDOperand & getOperand(unsigned I) const
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
unsigned getNumOperands() const
Return number of MDNode operands.
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
Flags
Flags values. These may be or'd together.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
A Module instance is used to store all the information related to an LLVM module.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
void addAssignPtrTypeInstr(Value *Val, CallInst *AssignPtrTyCI)
void buildAssignPtr(IRBuilder<> &B, Type *ElemTy, Value *Arg)
Type * findDeducedCompositeType(const Value *Val)
void replaceAllUsesWith(Value *Old, Value *New, bool DeleteOld=true)
void addDeducedElementType(Value *Val, Type *Ty)
void addReturnType(const Function *ArgF, TypedPointerType *DerivedTy)
Type * findMutated(const Value *Val)
void addDeducedCompositeType(Value *Val, Type *Ty)
void buildAssignType(IRBuilder<> &B, Type *Ty, Value *Arg)
Type * findDeducedElementType(const Value *Val)
void updateAssignType(CallInst *AssignCI, Value *Arg, Value *OfType)
CallInst * findAssignPtrTypeInstr(const Value *Val)
const SPIRVTargetLowering * getTargetLowering() const override
bool isLogicalSPIRV() const
bool canUseExtension(SPIRV::Extension::Extension E) const
const SPIRVSubtarget * getSubtargetImpl() const
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
void assign(size_type NumElts, ValueParamT Elt)
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
static unsigned getPointerOperandIndex()
iterator find(StringRef Key)
StringRef - Represent a constant reference to a string, i.e.
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
StringSet - A wrapper for StringMap that provides set-like functionality.
bool contains(StringRef key) const
Check if the set contains the given key.
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
static unsigned getPointerOperandIndex()
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.
const STC & getSubtarget(const Function &F) const
This method returns a pointer to the specified type of TargetSubtargetInfo.
The instances of the Type class are immutable: once they are created, they are never changed.
bool isVectorTy() const
True if this is an instance of VectorType.
bool isArrayTy() const
True if this is an instance of ArrayType.
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
bool isPointerTy() const
True if this is an instance of PointerType.
Type * getArrayElementType() const
LLVM_ABI StringRef getTargetExtName() const
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
bool isStructTy() const
True if this is an instance of StructType.
bool isTargetExtTy() const
Return true if this is a target extension type.
bool isAggregateType() const
Return true if the type is an aggregate type.
bool isIntegerTy() const
True if this is an instance of IntegerType.
static LLVM_ABI Type * getDoubleTy(LLVMContext &C)
Type * getContainedType(unsigned i) const
This method is used to implement the type iterator (defined at the end of the file).
static LLVM_ABI Type * getFloatTy(LLVMContext &C)
static LLVM_ABI Type * getHalfTy(LLVMContext &C)
bool isVoidTy() const
Return true if this is 'void'.
static LLVM_ABI bool isValidElementType(Type *ElemTy)
Return true if the specified type is valid as a element type.
static LLVM_ABI TypedPointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
void setOperand(unsigned i, Value *Val)
LLVM_ABI bool replaceUsesOfWith(Value *From, Value *To)
Replace uses of one Value with another.
Value * getOperand(unsigned i) const
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
user_iterator user_begin()
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
iterator_range< user_iterator > users()
void mutateType(Type *Ty)
Mutate the type of this Value to be of the specified type.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
std::pair< iterator, bool > insert(const ValueT &V)
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
const ParentTy * getParent() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
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.
@ SPIR_KERNEL
Used for SPIR kernel functions.
@ C
The default llvm calling convention, compatible with C.
@ BasicBlock
Various leaf nodes.
bool match(Val *V, const Pattern &P)
is_zero m_Zero()
Match any null constant or a vector with all elements equal to 0.
initializer< Ty > init(const Ty &Val)
@ User
could "use" a pointer
NodeAddr< PhiNode * > Phi
NodeAddr< NodeBase * > Node
NodeAddr< FuncNode * > Func
friend class Instruction
Iterator for Instructions in a `BasicBlock.
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
FunctionAddr VTableAddr Value
ModulePass * createSPIRVEmitIntrinsicsPass(const SPIRVTargetMachine &TM)
bool isTypedPointerWrapper(const TargetExtType *ExtTy)
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
unsigned getPointerAddressSpace(const Type *T)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
FunctionAddr VTableAddr uintptr_t uintptr_t Int32Ty
CallInst * buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef< Type * > Types, Value *Arg, Value *Arg2, ArrayRef< Constant * > Imms, IRBuilder<> &B)
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
bool isNestedPointer(const Type *Ty)
Function * getOrCreateBackendServiceFunction(Module &M)
MetadataAsValue * buildMD(Value *Arg)
std::string getOclOrSpirvBuiltinDemangledName(StringRef Name)
SmallVector< unsigned, 1 > getSpirvLoopControlOperandsFromLoopMetadata(MDNode *LoopMD)
auto reverse(ContainerTy &&C)
Type * getTypedPointerWrapper(Type *ElemTy, unsigned AS)
bool isPointerTy(const Type *T)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
bool set_union(S1Ty &S1, const S2Ty &S2)
set_union(A, B) - Compute A := A u B, return whether A changed.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
SPIRV::Scope::Scope getMemScope(LLVMContext &Ctx, SyncScope::ID Id)
@ Ref
The access may reference the value stored in memory.
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
@ And
Bitwise or logical AND of integers.
DWARFExpression::Operation Op
Type * getPointeeTypeByAttr(Argument *Arg)
bool hasPointeeTypeAttr(Argument *Arg)
constexpr unsigned BitWidth
bool isEquivalentTypes(Type *Ty1, Type *Ty2)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
iterator_range< pointer_iterator< WrappedIteratorT > > make_pointer_range(RangeT &&Range)
bool hasInitializer(const GlobalVariable *GV)
Type * normalizeType(Type *Ty)
@ Enabled
Convert any .debug_str_offsets tables to DWARF64 if needed.
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID)
PoisonValue * getNormalizedPoisonValue(Type *Ty)
bool isUntypedPointerTy(const Type *T)
Type * reconstitutePeeledArrayType(Type *Ty)
SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord)
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
static size_t computeFPFastMathDefaultInfoVecIndex(size_t BitWidth)