27#include "llvm/IR/IntrinsicsSPIRV.h"
35#include <unordered_set>
60 cl::desc(
"Emit OpName for all instructions"),
64#define GET_BuiltinGroup_DECL
65#include "SPIRVGenTables.inc"
70class GlobalVariableUsers {
71 template <
typename T1,
typename T2>
72 using OneToManyMapTy = DenseMap<T1, SmallPtrSet<T2, 4>>;
74 OneToManyMapTy<const GlobalVariable *, const Function *> GlobalIsUsedByFun;
76 void collectGlobalUsers(
77 const GlobalVariable *GV,
78 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
79 &GlobalIsUsedByGlobal) {
81 while (!
Stack.empty()) {
85 GlobalIsUsedByFun[GV].insert(
I->getFunction());
90 GlobalIsUsedByGlobal[GV].insert(UserGV);
95 Stack.append(
C->user_begin(),
C->user_end());
99 bool propagateGlobalToGlobalUsers(
100 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
101 &GlobalIsUsedByGlobal) {
104 for (
auto &[GV, UserGlobals] : GlobalIsUsedByGlobal) {
105 OldUsersGlobals.
assign(UserGlobals.begin(), UserGlobals.end());
106 for (
const GlobalVariable *UserGV : OldUsersGlobals) {
107 auto It = GlobalIsUsedByGlobal.find(UserGV);
108 if (It == GlobalIsUsedByGlobal.end())
116 void propagateGlobalToFunctionReferences(
117 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
118 &GlobalIsUsedByGlobal) {
119 for (
auto &[GV, UserGlobals] : GlobalIsUsedByGlobal) {
120 auto &UserFunctions = GlobalIsUsedByFun[GV];
121 for (
const GlobalVariable *UserGV : UserGlobals) {
122 auto It = GlobalIsUsedByFun.find(UserGV);
123 if (It == GlobalIsUsedByFun.end())
134 OneToManyMapTy<const GlobalVariable *, const GlobalVariable *>
135 GlobalIsUsedByGlobal;
136 GlobalIsUsedByFun.clear();
137 for (GlobalVariable &GV :
M.globals())
138 collectGlobalUsers(&GV, GlobalIsUsedByGlobal);
141 while (propagateGlobalToGlobalUsers(GlobalIsUsedByGlobal))
144 propagateGlobalToFunctionReferences(GlobalIsUsedByGlobal);
147 using FunctionSetType =
typename decltype(GlobalIsUsedByFun)::mapped_type;
148 const FunctionSetType &
149 getTransitiveUserFunctions(
const GlobalVariable &GV)
const {
150 auto It = GlobalIsUsedByFun.find(&GV);
151 if (It != GlobalIsUsedByFun.end())
154 static const FunctionSetType
Empty{};
159static bool isaGEP(
const Value *V) {
163class SPIRVEmitIntrinsics
165 public InstVisitor<SPIRVEmitIntrinsics, Instruction *> {
166 const SPIRVTargetMachine &TM;
167 SPIRVGlobalRegistry *GR =
nullptr;
169 bool TrackConstants =
true;
170 bool HaveFunPtrs =
false;
171 DenseMap<Instruction *, Constant *> AggrConsts;
172 DenseMap<Instruction *, Type *> AggrConstTypes;
173 DenseSet<Instruction *> AggrStores;
174 SmallPtrSet<Instruction *, 8> DeletedInstrs;
175 GlobalVariableUsers GVUsers;
176 std::unordered_set<Value *> Named;
179 DenseMap<Function *, SmallVector<std::pair<unsigned, Type *>>> FDeclPtrTys;
182 bool CanTodoType =
true;
183 unsigned TodoTypeSz = 0;
184 DenseMap<Value *, bool> TodoType;
185 void insertTodoType(
Value *
Op) {
187 if (CanTodoType && !isaGEP(
Op)) {
188 auto It = TodoType.try_emplace(
Op,
true);
193 void eraseTodoType(
Value *
Op) {
194 auto It = TodoType.find(
Op);
195 if (It != TodoType.end() && It->second) {
203 auto It = TodoType.find(
Op);
204 return It != TodoType.end() && It->second;
208 std::unordered_set<Instruction *> TypeValidated;
211 enum WellKnownTypes { Event };
214 Type *deduceElementType(
Value *
I,
bool UnknownElemTypeI8);
215 Type *deduceElementTypeHelper(
Value *
I,
bool UnknownElemTypeI8);
216 Type *deduceElementTypeHelper(
Value *
I, std::unordered_set<Value *> &Visited,
217 bool UnknownElemTypeI8,
218 bool IgnoreKnownType =
false);
219 Type *deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
220 bool UnknownElemTypeI8);
221 Type *deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
222 std::unordered_set<Value *> &Visited,
223 bool UnknownElemTypeI8);
225 std::unordered_set<Value *> &Visited,
226 bool UnknownElemTypeI8);
228 bool UnknownElemTypeI8);
231 Type *deduceNestedTypeHelper(User *U,
bool UnknownElemTypeI8);
232 Type *deduceNestedTypeHelper(User *U,
Type *Ty,
233 std::unordered_set<Value *> &Visited,
234 bool UnknownElemTypeI8);
237 void deduceOperandElementType(Instruction *
I,
238 SmallPtrSet<Instruction *, 4> *IncompleteRets,
239 const SmallPtrSet<Value *, 4> *AskOps =
nullptr,
240 bool IsPostprocessing =
false);
244 void simplifyNullAddrSpaceCasts();
246 Type *reconstructType(
Value *
Op,
bool UnknownElemTypeI8,
247 bool IsPostprocessing);
249 void replaceMemInstrUses(Instruction *Old, Instruction *New,
IRBuilder<> &
B);
251 bool insertAssignPtrTypeIntrs(Instruction *
I,
IRBuilder<> &
B,
252 bool UnknownElemTypeI8);
254 void insertAssignPtrTypeTargetExt(TargetExtType *AssignedType,
Value *V,
256 void replacePointerOperandWithPtrCast(Instruction *
I,
Value *Pointer,
257 Type *ExpectedElementType,
258 unsigned OperandToReplace,
260 void insertPtrCastOrAssignTypeInstr(Instruction *
I,
IRBuilder<> &
B);
261 bool shouldTryToAddMemAliasingDecoration(Instruction *Inst);
263 void insertConstantsForFPFastMathDefault(
Module &M);
265 void processGlobalValue(GlobalVariable &GV,
IRBuilder<> &
B);
267 void processParamTypesByFunHeader(Function *
F,
IRBuilder<> &
B);
268 Type *deduceFunParamElementType(Function *
F,
unsigned OpIdx);
269 Type *deduceFunParamElementType(Function *
F,
unsigned OpIdx,
270 std::unordered_set<Function *> &FVisited);
272 bool deduceOperandElementTypeCalledFunction(
274 Type *&KnownElemTy,
bool &Incomplete);
275 void deduceOperandElementTypeFunctionPointer(
277 Type *&KnownElemTy,
bool IsPostprocessing);
278 bool deduceOperandElementTypeFunctionRet(
279 Instruction *
I, SmallPtrSet<Instruction *, 4> *IncompleteRets,
280 const SmallPtrSet<Value *, 4> *AskOps,
bool IsPostprocessing,
283 CallInst *buildSpvPtrcast(Function *
F,
Value *
Op,
Type *ElemTy);
284 void replaceUsesOfWithSpvPtrcast(
Value *
Op,
Type *ElemTy, Instruction *
I,
285 DenseMap<Function *, CallInst *> Ptrcasts);
287 DenseSet<std::pair<Value *, Value *>> &VisitedSubst);
290 DenseSet<std::pair<Value *, Value *>> &VisitedSubst);
291 void propagateElemTypeRec(
Value *
Op,
Type *PtrElemTy,
Type *CastElemTy,
292 DenseSet<std::pair<Value *, Value *>> &VisitedSubst,
293 std::unordered_set<Value *> &Visited,
294 DenseMap<Function *, CallInst *> Ptrcasts);
297 void replaceAllUsesWithAndErase(
IRBuilder<> &
B, Instruction *Src,
298 Instruction *Dest,
bool DeleteOld =
true);
302 GetElementPtrInst *simplifyZeroLengthArrayGepInst(GetElementPtrInst *
GEP);
305 bool postprocessTypes(
Module &M);
306 bool processFunctionPointers(
Module &M);
307 void parseFunDeclarations(
Module &M);
308 void useRoundingMode(ConstrainedFPIntrinsic *FPI,
IRBuilder<> &
B);
309 bool processMaskedMemIntrinsic(IntrinsicInst &
I);
310 bool convertMaskedMemIntrinsics(
Module &M);
311 void preprocessBoolVectorBitcasts(Function &
F);
313 void emitUnstructuredLoopControls(Function &
F,
IRBuilder<> &
B);
329 bool walkLogicalAccessChain(
330 GetElementPtrInst &
GEP,
331 const std::function<
void(
Type *PointedType, uint64_t Index)>
340 Type *getGEPType(GetElementPtrInst *
GEP);
347 Type *getGEPTypeLogical(GetElementPtrInst *
GEP);
349 Instruction *buildLogicalAccessChainFromGEP(GetElementPtrInst &
GEP);
353 SPIRVEmitIntrinsics(
const SPIRVTargetMachine &TM) : ModulePass(ID), TM(TM) {}
356 Instruction *visitGetElementPtrInst(GetElementPtrInst &
I);
359 Instruction *visitInsertElementInst(InsertElementInst &
I);
360 Instruction *visitExtractElementInst(ExtractElementInst &
I);
362 Instruction *visitExtractValueInst(ExtractValueInst &
I);
366 Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &
I);
370 StringRef getPassName()
const override {
return "SPIRV emit intrinsics"; }
372 bool runOnModule(
Module &M)
override;
374 void getAnalysisUsage(AnalysisUsage &AU)
const override {
375 ModulePass::getAnalysisUsage(AU);
381 Intrinsic::experimental_convergence_loop,
382 Intrinsic::experimental_convergence_anchor>());
385bool expectIgnoredInIRTranslation(
const Instruction *
I) {
387 Intrinsic::spv_resource_handlefrombinding,
388 Intrinsic::spv_resource_getpointer>());
395 return getPointerRoot(V);
401char SPIRVEmitIntrinsics::ID = 0;
404 "SPIRV emit intrinsics",
false,
false)
418 bool IsUndefAggregate =
isa<UndefValue>(V) && V->getType()->isAggregateType();
425 B.SetInsertPoint(
I->getParent()->getFirstNonPHIOrDbgOrAlloca());
431 B.SetCurrentDebugLocation(
I->getDebugLoc());
432 if (
I->getType()->isVoidTy())
433 B.SetInsertPoint(
I->getNextNode());
435 B.SetInsertPoint(*
I->getInsertionPointAfterDef());
445 if (
I->getType()->isTokenTy())
447 "does not support token type",
452 if (!
I->hasName() ||
I->getType()->isAggregateType() ||
453 expectIgnoredInIRTranslation(
I))
464 if (
F &&
F->getName().starts_with(
"llvm.spv.alloca"))
475 std::vector<Value *> Args = {
478 B.CreateIntrinsic(Intrinsic::spv_assign_name, {
I->getType()}, Args);
481void SPIRVEmitIntrinsics::replaceAllUsesWith(
Value *Src,
Value *Dest,
485 if (isTodoType(Src)) {
488 insertTodoType(Dest);
492void SPIRVEmitIntrinsics::replaceAllUsesWithAndErase(
IRBuilder<> &
B,
497 std::string
Name = Src->hasName() ? Src->getName().str() :
"";
498 Src->eraseFromParent();
501 if (Named.insert(Dest).second)
526Type *SPIRVEmitIntrinsics::reconstructType(
Value *
Op,
bool UnknownElemTypeI8,
527 bool IsPostprocessing) {
542 if (UnknownElemTypeI8) {
543 if (!IsPostprocessing)
551CallInst *SPIRVEmitIntrinsics::buildSpvPtrcast(Function *
F,
Value *
Op,
559 B.SetInsertPointPastAllocas(OpA->getParent());
562 B.SetInsertPoint(
F->getEntryBlock().getFirstNonPHIOrDbgOrAlloca());
564 Type *OpTy =
Op->getType();
568 CallInst *PtrCasted =
569 B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
574void SPIRVEmitIntrinsics::replaceUsesOfWithSpvPtrcast(
576 DenseMap<Function *, CallInst *> Ptrcasts) {
578 CallInst *PtrCastedI =
nullptr;
579 auto It = Ptrcasts.
find(
F);
580 if (It == Ptrcasts.
end()) {
581 PtrCastedI = buildSpvPtrcast(
F,
Op, ElemTy);
582 Ptrcasts[
F] = PtrCastedI;
584 PtrCastedI = It->second;
586 I->replaceUsesOfWith(
Op, PtrCastedI);
589void SPIRVEmitIntrinsics::propagateElemType(
591 DenseSet<std::pair<Value *, Value *>> &VisitedSubst) {
592 DenseMap<Function *, CallInst *> Ptrcasts;
594 for (
auto *U :
Users) {
597 if (!VisitedSubst.insert(std::make_pair(U,
Op)).second)
602 if (isaGEP(UI) || TypeValidated.find(UI) != TypeValidated.end())
603 replaceUsesOfWithSpvPtrcast(
Op, ElemTy, UI, Ptrcasts);
607void SPIRVEmitIntrinsics::propagateElemTypeRec(
609 DenseSet<std::pair<Value *, Value *>> &VisitedSubst) {
610 std::unordered_set<Value *> Visited;
611 DenseMap<Function *, CallInst *> Ptrcasts;
612 propagateElemTypeRec(
Op, PtrElemTy, CastElemTy, VisitedSubst, Visited,
613 std::move(Ptrcasts));
616void SPIRVEmitIntrinsics::propagateElemTypeRec(
618 DenseSet<std::pair<Value *, Value *>> &VisitedSubst,
619 std::unordered_set<Value *> &Visited,
620 DenseMap<Function *, CallInst *> Ptrcasts) {
621 if (!Visited.insert(
Op).second)
624 for (
auto *U :
Users) {
627 if (!VisitedSubst.insert(std::make_pair(U,
Op)).second)
632 if (isaGEP(UI) || TypeValidated.find(UI) != TypeValidated.end())
633 replaceUsesOfWithSpvPtrcast(
Op, CastElemTy, UI, Ptrcasts);
641SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
642 bool UnknownElemTypeI8) {
643 std::unordered_set<Value *> Visited;
644 return deduceElementTypeByValueDeep(ValueTy, Operand, Visited,
648Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
649 Type *ValueTy,
Value *Operand, std::unordered_set<Value *> &Visited,
650 bool UnknownElemTypeI8) {
655 deduceElementTypeHelper(Operand, Visited, UnknownElemTypeI8))
666Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
667 Value *
Op, std::unordered_set<Value *> &Visited,
bool UnknownElemTypeI8) {
679 for (User *OpU :
Op->users()) {
681 if (
Type *Ty = deduceElementTypeHelper(Inst, Visited, UnknownElemTypeI8))
694 if ((DemangledName.
starts_with(
"__spirv_ocl_printf(") ||
703Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
Value *
I,
704 bool UnknownElemTypeI8) {
705 std::unordered_set<Value *> Visited;
706 return deduceElementTypeHelper(
I, Visited, UnknownElemTypeI8);
709void SPIRVEmitIntrinsics::maybeAssignPtrType(
Type *&Ty,
Value *
Op,
Type *RefTy,
710 bool UnknownElemTypeI8) {
712 if (!UnknownElemTypeI8)
719bool SPIRVEmitIntrinsics::walkLogicalAccessChain(
720 GetElementPtrInst &
GEP,
721 const std::function<
void(
Type *, uint64_t)> &OnLiteralIndexing,
722 const std::function<
void(
Type *,
Value *)> &OnDynamicIndexing) {
730 Value *Src = getPointerRoot(
GEP.getPointerOperand());
731 Type *CurType = deduceElementType(Src,
true);
740 OnDynamicIndexing(AT->getElementType(), Operand);
741 return AT ==
nullptr;
749 uint32_t EltTypeSize =
DL.getTypeSizeInBits(AT->getElementType()) / 8;
753 CurType = AT->getElementType();
754 OnLiteralIndexing(CurType, Index);
756 uint32_t StructSize =
DL.getTypeSizeInBits(ST) / 8;
759 const auto &STL =
DL.getStructLayout(ST);
760 unsigned Element = STL->getElementContainingOffset(
Offset);
761 Offset -= STL->getElementOffset(Element);
762 CurType =
ST->getElementType(Element);
763 OnLiteralIndexing(CurType, Element);
765 Type *EltTy = VT->getElementType();
766 TypeSize EltSizeBits =
DL.getTypeSizeInBits(EltTy);
767 assert(EltSizeBits % 8 == 0 &&
768 "Element type size in bits must be a multiple of 8.");
769 uint32_t EltTypeSize = EltSizeBits / 8;
774 OnLiteralIndexing(CurType, Index);
786SPIRVEmitIntrinsics::buildLogicalAccessChainFromGEP(GetElementPtrInst &
GEP) {
789 B.SetInsertPoint(&
GEP);
791 std::vector<Value *> Indices;
792 Indices.push_back(ConstantInt::get(
793 IntegerType::getInt32Ty(CurrF->
getContext()), 0,
false));
794 walkLogicalAccessChain(
796 [&Indices, &
B](
Type *EltType, uint64_t Index) {
798 ConstantInt::get(
B.getInt64Ty(), Index,
false));
801 uint32_t EltTypeSize =
DL.getTypeSizeInBits(EltType) / 8;
803 Offset, ConstantInt::get(
Offset->getType(), EltTypeSize,
805 Indices.push_back(Index);
809 SmallVector<Value *, 4>
Args;
810 Args.push_back(
B.getInt1(
GEP.isInBounds()));
811 Args.push_back(
GEP.getOperand(0));
813 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
814 replaceAllUsesWithAndErase(
B, &
GEP, NewI);
818Type *SPIRVEmitIntrinsics::getGEPTypeLogical(GetElementPtrInst *
GEP) {
820 Type *CurType =
GEP->getResultElementType();
822 bool Interrupted = walkLogicalAccessChain(
823 *
GEP, [&CurType](
Type *EltType, uint64_t Index) { CurType = EltType; },
826 return Interrupted ?
GEP->getResultElementType() : CurType;
829Type *SPIRVEmitIntrinsics::getGEPType(GetElementPtrInst *
Ref) {
830 if (
Ref->getSourceElementType() ==
831 IntegerType::getInt8Ty(CurrF->
getContext()) &&
833 return getGEPTypeLogical(
Ref);
840 Ty =
Ref->getSourceElementType();
844 Ty =
Ref->getResultElementType();
849Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
850 Value *
I, std::unordered_set<Value *> &Visited,
bool UnknownElemTypeI8,
851 bool IgnoreKnownType) {
857 if (!IgnoreKnownType)
862 if (!Visited.insert(
I).second)
869 maybeAssignPtrType(Ty,
I,
Ref->getAllocatedType(), UnknownElemTypeI8);
871 Ty = getGEPType(
Ref);
873 Ty = SGEP->getResultElementType();
878 KnownTy =
Op->getType();
880 maybeAssignPtrType(Ty,
I, ElemTy, UnknownElemTypeI8);
883 Ty = SPIRV::getOriginalFunctionType(*Fn);
886 Ty = deduceElementTypeByValueDeep(
888 Ref->getNumOperands() > 0 ?
Ref->getOperand(0) :
nullptr, Visited,
892 Type *RefTy = deduceElementTypeHelper(
Ref->getPointerOperand(), Visited,
894 maybeAssignPtrType(Ty,
I, RefTy, UnknownElemTypeI8);
896 maybeAssignPtrType(Ty,
I,
Ref->getDestTy(), UnknownElemTypeI8);
898 if (
Type *Src =
Ref->getSrcTy(), *Dest =
Ref->getDestTy();
900 Ty = deduceElementTypeHelper(
Ref->getOperand(0), Visited,
905 Ty = deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8);
909 Ty = deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8);
911 Type *BestTy =
nullptr;
913 DenseMap<Type *, unsigned> PhiTys;
914 for (
int i =
Ref->getNumIncomingValues() - 1; i >= 0; --i) {
915 Ty = deduceElementTypeByUsersDeep(
Ref->getIncomingValue(i), Visited,
922 if (It.first->second > MaxN) {
923 MaxN = It.first->second;
931 for (
Value *
Op : {
Ref->getTrueValue(),
Ref->getFalseValue()}) {
932 Ty = deduceElementTypeByUsersDeep(
Op, Visited, UnknownElemTypeI8);
937 static StringMap<unsigned> ResTypeByArg = {
941 {
"__spirv_GenericCastToPtr_ToGlobal", 0},
942 {
"__spirv_GenericCastToPtr_ToLocal", 0},
943 {
"__spirv_GenericCastToPtr_ToPrivate", 0},
944 {
"__spirv_GenericCastToPtrExplicit_ToGlobal", 0},
945 {
"__spirv_GenericCastToPtrExplicit_ToLocal", 0},
946 {
"__spirv_GenericCastToPtrExplicit_ToPrivate", 0}};
950 if (
II &&
II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
952 if (HandleType->getTargetExtName() ==
"spirv.Image" ||
953 HandleType->getTargetExtName() ==
"spirv.SignedImage") {
954 for (User *U :
II->users()) {
959 }
else if (HandleType->getTargetExtName() ==
"spirv.VulkanBuffer") {
961 Ty = HandleType->getTypeParameter(0);
973 }
else if (
II &&
II->getIntrinsicID() ==
974 Intrinsic::spv_generic_cast_to_ptr_explicit) {
975 Ty = deduceElementTypeHelper(CI->getArgOperand(0), Visited,
977 }
else if (Function *CalledF = CI->getCalledFunction()) {
978 std::string DemangledName =
980 if (DemangledName.length() > 0)
981 DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName);
982 auto AsArgIt = ResTypeByArg.
find(DemangledName);
983 if (AsArgIt != ResTypeByArg.
end())
984 Ty = deduceElementTypeHelper(CI->getArgOperand(AsArgIt->second),
985 Visited, UnknownElemTypeI8);
992 if (Ty && !IgnoreKnownType) {
1003Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U,
1004 bool UnknownElemTypeI8) {
1005 std::unordered_set<Value *> Visited;
1006 return deduceNestedTypeHelper(U,
U->getType(), Visited, UnknownElemTypeI8);
1009Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
1010 User *U,
Type *OrigTy, std::unordered_set<Value *> &Visited,
1011 bool UnknownElemTypeI8) {
1020 if (!Visited.insert(U).second)
1025 bool Change =
false;
1026 for (
unsigned i = 0; i <
U->getNumOperands(); ++i) {
1028 assert(
Op &&
"Operands should not be null.");
1029 Type *OpTy =
Op->getType();
1032 if (
Type *NestedTy =
1033 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
1040 Change |= Ty != OpTy;
1048 if (
Value *
Op =
U->getNumOperands() > 0 ?
U->getOperand(0) :
nullptr) {
1049 Type *OpTy = ArrTy->getElementType();
1052 if (
Type *NestedTy =
1053 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
1060 Type *NewTy = ArrayType::get(Ty, ArrTy->getNumElements());
1066 if (
Value *
Op =
U->getNumOperands() > 0 ?
U->getOperand(0) :
nullptr) {
1067 Type *OpTy = VecTy->getElementType();
1070 if (
Type *NestedTy =
1071 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
1078 Type *NewTy = VectorType::get(Ty, VecTy->getElementCount());
1088Type *SPIRVEmitIntrinsics::deduceElementType(
Value *
I,
bool UnknownElemTypeI8) {
1089 if (
Type *Ty = deduceElementTypeHelper(
I, UnknownElemTypeI8))
1091 if (!UnknownElemTypeI8)
1094 return IntegerType::getInt8Ty(
I->getContext());
1098 Value *PointerOperand) {
1104 return I->getType();
1112bool SPIRVEmitIntrinsics::deduceOperandElementTypeCalledFunction(
1114 Type *&KnownElemTy,
bool &Incomplete) {
1118 std::string DemangledName =
1120 if (DemangledName.length() > 0 &&
1122 const SPIRVSubtarget &
ST = TM.
getSubtarget<SPIRVSubtarget>(*CalledF);
1123 auto [Grp, Opcode, ExtNo] = SPIRV::mapBuiltinToOpcode(
1124 DemangledName,
ST.getPreferredInstructionSet());
1125 if (Opcode == SPIRV::OpGroupAsyncCopy) {
1126 for (
unsigned i = 0, PtrCnt = 0; i < CI->
arg_size() && PtrCnt < 2; ++i) {
1132 KnownElemTy = ElemTy;
1133 Ops.push_back(std::make_pair(
Op, i));
1135 }
else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) {
1142 case SPIRV::OpAtomicFAddEXT:
1143 case SPIRV::OpAtomicFMinEXT:
1144 case SPIRV::OpAtomicFMaxEXT:
1145 case SPIRV::OpAtomicLoad:
1146 case SPIRV::OpAtomicCompareExchangeWeak:
1147 case SPIRV::OpAtomicCompareExchange:
1148 case SPIRV::OpAtomicExchange:
1149 case SPIRV::OpAtomicIAdd:
1150 case SPIRV::OpAtomicISub:
1151 case SPIRV::OpAtomicOr:
1152 case SPIRV::OpAtomicXor:
1153 case SPIRV::OpAtomicAnd:
1154 case SPIRV::OpAtomicUMin:
1155 case SPIRV::OpAtomicUMax:
1156 case SPIRV::OpAtomicSMin:
1157 case SPIRV::OpAtomicSMax: {
1162 Incomplete = isTodoType(
Op);
1163 Ops.push_back(std::make_pair(
Op, 0));
1165 case SPIRV::OpAtomicStore: {
1174 Incomplete = isTodoType(
Op);
1175 Ops.push_back(std::make_pair(
Op, 0));
1184void SPIRVEmitIntrinsics::deduceOperandElementTypeFunctionPointer(
1186 Type *&KnownElemTy,
bool IsPostprocessing) {
1190 Ops.push_back(std::make_pair(
Op, std::numeric_limits<unsigned>::max()));
1191 FunctionType *FTy = SPIRV::getOriginalFunctionType(*CI);
1192 bool IsNewFTy =
false, IsIncomplete =
false;
1195 Type *ArgTy = Arg->getType();
1200 if (isTodoType(Arg))
1201 IsIncomplete =
true;
1203 IsIncomplete =
true;
1206 ArgTy = FTy->getFunctionParamType(ParmIdx);
1210 Type *RetTy = FTy->getReturnType();
1217 IsIncomplete =
true;
1219 IsIncomplete =
true;
1222 if (!IsPostprocessing && IsIncomplete)
1225 IsNewFTy ? FunctionType::get(RetTy, ArgTys, FTy->isVarArg()) : FTy;
1228bool SPIRVEmitIntrinsics::deduceOperandElementTypeFunctionRet(
1229 Instruction *
I, SmallPtrSet<Instruction *, 4> *IncompleteRets,
1230 const SmallPtrSet<Value *, 4> *AskOps,
bool IsPostprocessing,
1242 DenseSet<std::pair<Value *, Value *>> VisitedSubst{std::make_pair(
I,
Op)};
1243 for (User *U :
F->users()) {
1251 propagateElemType(CI, PrevElemTy, VisitedSubst);
1261 for (Instruction *IncompleteRetI : *IncompleteRets)
1262 deduceOperandElementType(IncompleteRetI,
nullptr, AskOps,
1264 }
else if (IncompleteRets) {
1267 TypeValidated.insert(
I);
1275void SPIRVEmitIntrinsics::deduceOperandElementType(
1276 Instruction *
I, SmallPtrSet<Instruction *, 4> *IncompleteRets,
1277 const SmallPtrSet<Value *, 4> *AskOps,
bool IsPostprocessing) {
1279 Type *KnownElemTy =
nullptr;
1280 bool Incomplete =
false;
1286 Incomplete = isTodoType(
I);
1287 for (
unsigned i = 0; i <
Ref->getNumIncomingValues(); i++) {
1290 Ops.push_back(std::make_pair(
Op, i));
1296 Incomplete = isTodoType(
I);
1297 Ops.push_back(std::make_pair(
Ref->getPointerOperand(), 0));
1304 Incomplete = isTodoType(
I);
1305 Ops.push_back(std::make_pair(
Ref->getOperand(0), 0));
1309 KnownElemTy =
Ref->getSourceElementType();
1310 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1315 KnownElemTy =
Ref->getBaseType();
1316 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1319 KnownElemTy =
I->getType();
1325 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1329 reconstructType(
Ref->getValueOperand(),
false, IsPostprocessing)))
1334 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1342 Incomplete = isTodoType(
Ref->getPointerOperand());
1343 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1351 Incomplete = isTodoType(
Ref->getPointerOperand());
1352 Ops.push_back(std::make_pair(
Ref->getPointerOperand(),
1358 Incomplete = isTodoType(
I);
1359 for (
unsigned i = 0; i <
Ref->getNumOperands(); i++) {
1362 Ops.push_back(std::make_pair(
Op, i));
1370 if (deduceOperandElementTypeFunctionRet(
I, IncompleteRets, AskOps,
1371 IsPostprocessing, KnownElemTy,
Op,
1374 Incomplete = isTodoType(CurrF);
1375 Ops.push_back(std::make_pair(
Op, 0));
1381 bool Incomplete0 = isTodoType(Op0);
1382 bool Incomplete1 = isTodoType(Op1);
1384 Type *ElemTy0 = (Incomplete0 && !Incomplete1 && ElemTy1)
1386 : GR->findDeducedElementType(Op0);
1388 KnownElemTy = ElemTy0;
1389 Incomplete = Incomplete0;
1390 Ops.push_back(std::make_pair(Op1, 1));
1391 }
else if (ElemTy1) {
1392 KnownElemTy = ElemTy1;
1393 Incomplete = Incomplete1;
1394 Ops.push_back(std::make_pair(Op0, 0));
1398 deduceOperandElementTypeCalledFunction(CI,
Ops, KnownElemTy, Incomplete);
1399 else if (HaveFunPtrs)
1400 deduceOperandElementTypeFunctionPointer(CI,
Ops, KnownElemTy,
1405 if (!KnownElemTy ||
Ops.size() == 0)
1410 for (
auto &OpIt :
Ops) {
1414 Type *AskTy =
nullptr;
1415 CallInst *AskCI =
nullptr;
1416 if (IsPostprocessing && AskOps) {
1422 if (Ty == KnownElemTy)
1425 Type *OpTy =
Op->getType();
1426 if (
Op->hasUseList() &&
1433 else if (!IsPostprocessing)
1437 if (AssignCI ==
nullptr) {
1446 DenseSet<std::pair<Value *, Value *>> VisitedSubst{
1447 std::make_pair(
I,
Op)};
1448 propagateElemTypeRec(
Op, KnownElemTy, PrevElemTy, VisitedSubst);
1452 CallInst *PtrCastI =
1453 buildSpvPtrcast(
I->getParent()->getParent(),
Op, KnownElemTy);
1454 if (OpIt.second == std::numeric_limits<unsigned>::max())
1457 I->setOperand(OpIt.second, PtrCastI);
1460 TypeValidated.insert(
I);
1463void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old,
1468 if (isAssignTypeInstr(U)) {
1469 B.SetInsertPoint(U);
1470 SmallVector<Value *, 2>
Args = {
New,
U->getOperand(1)};
1471 CallInst *AssignCI =
1472 B.CreateIntrinsic(Intrinsic::spv_assign_type, {
New->getType()},
Args);
1474 U->eraseFromParent();
1477 U->replaceUsesOfWith(Old, New);
1479 if (
Phi->getType() !=
New->getType()) {
1480 Phi->mutateType(
New->getType());
1481 Phi->replaceUsesOfWith(Old, New);
1484 for (User *PhiUser :
Phi->users())
1487 for (ExtractValueInst *EV : EVUsers) {
1488 B.SetInsertPoint(EV);
1490 for (
unsigned Idx : EV->indices())
1491 Args.push_back(
B.getInt32(Idx));
1493 B.CreateIntrinsic(Intrinsic::spv_extractv, {EV->getType()},
Args);
1494 EV->replaceAllUsesWith(NewEV);
1495 DeletedInstrs.
insert(EV);
1496 EV->eraseFromParent();
1499 Phi->replaceUsesOfWith(Old, New);
1505 New->copyMetadata(*Old);
1509void SPIRVEmitIntrinsics::preprocessUndefs(
IRBuilder<> &
B) {
1510 std::queue<Instruction *> Worklist;
1514 while (!Worklist.empty()) {
1516 bool BPrepared =
false;
1519 for (
auto &
Op :
I->operands()) {
1521 if (!AggrUndef || !
Op->getType()->isAggregateType())
1528 auto *IntrUndef =
B.CreateIntrinsic(Intrinsic::spv_undef, {});
1529 Worklist.push(IntrUndef);
1530 I->replaceUsesOfWith(
Op, IntrUndef);
1531 AggrConsts[IntrUndef] = AggrUndef;
1532 AggrConstTypes[IntrUndef] = AggrUndef->getType();
1541void SPIRVEmitIntrinsics::simplifyNullAddrSpaceCasts() {
1545 ASC->replaceAllUsesWith(
1547 ASC->eraseFromParent();
1551void SPIRVEmitIntrinsics::preprocessCompositeConstants(
IRBuilder<> &
B) {
1552 std::queue<Instruction *> Worklist;
1556 while (!Worklist.empty()) {
1557 auto *
I = Worklist.front();
1560 bool KeepInst =
false;
1561 for (
const auto &
Op :
I->operands()) {
1563 Type *ResTy =
nullptr;
1566 ResTy = COp->getType();
1578 ResTy =
Op->getType()->isVectorTy() ? COp->getType() :
B.getInt32Ty();
1583 for (
unsigned i = 0; i < COp->getNumElements(); ++i)
1584 Args.push_back(COp->getElementAsConstant(i));
1590 CE &&
CE->getOpcode() == Instruction::AddrSpaceCast &&
1596 IsPhi ?
B.SetInsertPointPastAllocas(
I->getParent()->getParent())
1597 :
B.SetInsertPoint(
I);
1601 B.CreateIntrinsic(Intrinsic::spv_const_composite, {ResTy}, {
Args});
1605 AggrConsts[CI] = AggrConst;
1606 AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst,
false);
1618 B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {
I->getType()},
1623 unsigned RoundingModeDeco,
1630 ConstantInt::get(
Int32Ty, SPIRV::Decoration::FPRoundingMode)),
1639 MDNode *SaturatedConversionNode =
1641 Int32Ty, SPIRV::Decoration::SaturatedConversion))});
1658 MDString *ConstraintString =
MDString::get(Ctx,
IA->getConstraintString());
1666 B.SetInsertPoint(&
Call);
1667 B.CreateIntrinsic(Intrinsic::spv_inline_asm, {
Args});
1672void SPIRVEmitIntrinsics::useRoundingMode(ConstrainedFPIntrinsic *FPI,
1675 if (!
RM.has_value())
1677 unsigned RoundingModeDeco = std::numeric_limits<unsigned>::max();
1678 switch (
RM.value()) {
1682 case RoundingMode::NearestTiesToEven:
1683 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTE;
1685 case RoundingMode::TowardNegative:
1686 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTN;
1688 case RoundingMode::TowardPositive:
1689 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTP;
1691 case RoundingMode::TowardZero:
1692 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTZ;
1694 case RoundingMode::Dynamic:
1695 case RoundingMode::NearestTiesToAway:
1699 if (RoundingModeDeco == std::numeric_limits<unsigned>::max())
1705Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &
I) {
1709 B.SetInsertPoint(&
I);
1710 SmallVector<Value *, 4>
Args;
1712 Args.push_back(
I.getCondition());
1715 for (
auto &Case :
I.cases()) {
1716 Args.push_back(Case.getCaseValue());
1717 BBCases.
push_back(Case.getCaseSuccessor());
1720 CallInst *NewI =
B.CreateIntrinsic(Intrinsic::spv_switch,
1721 {
I.getOperand(0)->getType()}, {
Args});
1725 I.eraseFromParent();
1728 B.SetInsertPoint(ParentBB);
1729 IndirectBrInst *BrI =
B.CreateIndirectBr(
1732 for (BasicBlock *BBCase : BBCases)
1741Instruction *SPIRVEmitIntrinsics::visitIntrinsicInst(IntrinsicInst &
I) {
1747 B.SetInsertPoint(&
I);
1749 SmallVector<Value *, 4>
Args;
1750 Args.push_back(
B.getInt1(
true));
1751 Args.push_back(
I.getOperand(0));
1752 Args.push_back(
B.getInt32(0));
1753 for (
unsigned J = 0; J < SGEP->getNumIndices(); ++J)
1754 Args.push_back(SGEP->getIndexOperand(J));
1756 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, Types, Args);
1757 replaceAllUsesWithAndErase(
B, &
I, NewI);
1761Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &
I) {
1763 B.SetInsertPoint(&
I);
1771 if (
I.getSourceElementType() ==
1772 IntegerType::getInt8Ty(CurrF->
getContext())) {
1773 return buildLogicalAccessChainFromGEP(
I);
1778 Value *PtrOp =
I.getPointerOperand();
1779 Type *SrcElemTy =
I.getSourceElementType();
1780 Type *DeducedPointeeTy = deduceElementType(PtrOp,
true);
1783 if (ArrTy->getElementType() == SrcElemTy) {
1785 Type *FirstIdxType =
I.getOperand(1)->getType();
1786 NewIndices.
push_back(ConstantInt::get(FirstIdxType, 0));
1787 for (
Value *Idx :
I.indices())
1791 SmallVector<Value *, 4>
Args;
1792 Args.push_back(
B.getInt1(
I.isInBounds()));
1793 Args.push_back(
I.getPointerOperand());
1796 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
1797 replaceAllUsesWithAndErase(
B, &
I, NewI);
1804 SmallVector<Value *, 4>
Args;
1805 Args.push_back(
B.getInt1(
I.isInBounds()));
1807 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
1808 replaceAllUsesWithAndErase(
B, &
I, NewI);
1812Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &
I) {
1814 B.SetInsertPoint(&
I);
1823 I.eraseFromParent();
1829 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_bitcast, {
Types}, {
Args});
1830 replaceAllUsesWithAndErase(
B, &
I, NewI);
1834void SPIRVEmitIntrinsics::insertAssignPtrTypeTargetExt(
1836 Type *VTy =
V->getType();
1841 if (ElemTy != AssignedType)
1854 if (CurrentType == AssignedType)
1861 " for value " +
V->getName(),
1869void SPIRVEmitIntrinsics::replacePointerOperandWithPtrCast(
1870 Instruction *
I,
Value *Pointer,
Type *ExpectedElementType,
1872 TypeValidated.insert(
I);
1875 Type *PointerElemTy = deduceElementTypeHelper(Pointer,
false);
1876 if (PointerElemTy == ExpectedElementType ||
1882 MetadataAsValue *VMD =
buildMD(ExpectedElementVal);
1884 bool FirstPtrCastOrAssignPtrType =
true;
1890 for (
auto User :
Pointer->users()) {
1893 (
II->getIntrinsicID() != Intrinsic::spv_assign_ptr_type &&
1894 II->getIntrinsicID() != Intrinsic::spv_ptrcast) ||
1895 II->getOperand(0) != Pointer)
1900 FirstPtrCastOrAssignPtrType =
false;
1901 if (
II->getOperand(1) != VMD ||
1908 if (
II->getIntrinsicID() != Intrinsic::spv_ptrcast)
1913 if (
II->getParent() !=
I->getParent())
1916 I->setOperand(OperandToReplace,
II);
1922 if (FirstPtrCastOrAssignPtrType) {
1927 }
else if (isTodoType(Pointer)) {
1928 eraseTodoType(Pointer);
1936 DenseSet<std::pair<Value *, Value *>> VisitedSubst{
1937 std::make_pair(
I, Pointer)};
1939 propagateElemType(Pointer, PrevElemTy, VisitedSubst);
1951 auto *PtrCastI =
B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
1957void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *
I,
1962 replacePointerOperandWithPtrCast(
1963 I,
SI->getValueOperand(), IntegerType::getInt8Ty(CurrF->
getContext()),
1969 Type *OpTy =
Op->getType();
1972 if (OpTy ==
Op->getType())
1973 OpTy = deduceElementTypeByValueDeep(OpTy,
Op,
false);
1974 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 1,
B);
1979 Type *OpTy = LI->getType();
1984 Type *NewOpTy = OpTy;
1985 OpTy = deduceElementTypeByValueDeep(OpTy, LI,
false);
1986 if (OpTy == NewOpTy)
1987 insertTodoType(Pointer);
1990 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 0,
B);
1995 Type *OpTy =
nullptr;
2007 OpTy = GEPI->getSourceElementType();
2009 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 0,
B);
2011 insertTodoType(Pointer);
2023 std::string DemangledName =
2027 bool HaveTypes =
false;
2045 for (User *U : CalledArg->
users()) {
2047 if ((ElemTy = deduceElementTypeHelper(Inst,
false)) !=
nullptr)
2053 HaveTypes |= ElemTy !=
nullptr;
2058 if (DemangledName.empty() && !HaveTypes)
2076 Type *ExpectedType =
2078 if (!ExpectedType && !DemangledName.empty())
2079 ExpectedType = SPIRV::parseBuiltinCallArgumentBaseType(
2080 DemangledName,
OpIdx,
I->getContext());
2081 if (!ExpectedType || ExpectedType->
isVoidTy())
2089 replacePointerOperandWithPtrCast(CI, ArgOperand, ExpectedType,
OpIdx,
B);
2093Instruction *SPIRVEmitIntrinsics::visitInsertElementInst(InsertElementInst &
I) {
2100 I.getOperand(1)->getType(),
2101 I.getOperand(2)->getType()};
2103 B.SetInsertPoint(&
I);
2105 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_insertelt, {
Types}, {
Args});
2106 replaceAllUsesWithAndErase(
B, &
I, NewI);
2111SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &
I) {
2118 B.SetInsertPoint(&
I);
2120 I.getIndexOperand()->getType()};
2121 SmallVector<Value *, 2>
Args = {
I.getVectorOperand(),
I.getIndexOperand()};
2122 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_extractelt, {
Types}, {
Args});
2123 replaceAllUsesWithAndErase(
B, &
I, NewI);
2127Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &
I) {
2129 B.SetInsertPoint(&
I);
2132 Value *AggregateOp =
I.getAggregateOperand();
2136 Args.push_back(AggregateOp);
2137 Args.push_back(
I.getInsertedValueOperand());
2138 for (
auto &
Op :
I.indices())
2139 Args.push_back(
B.getInt32(
Op));
2141 B.CreateIntrinsic(Intrinsic::spv_insertv, {
Types}, {
Args});
2142 replaceMemInstrUses(&
I, NewI,
B);
2146Instruction *SPIRVEmitIntrinsics::visitExtractValueInst(ExtractValueInst &
I) {
2147 if (
I.getAggregateOperand()->getType()->isAggregateType())
2150 B.SetInsertPoint(&
I);
2152 for (
auto &
Op :
I.indices())
2153 Args.push_back(
B.getInt32(
Op));
2155 B.CreateIntrinsic(Intrinsic::spv_extractv, {
I.getType()}, {
Args});
2156 replaceAllUsesWithAndErase(
B, &
I, NewI);
2160Instruction *SPIRVEmitIntrinsics::visitLoadInst(LoadInst &
I) {
2161 if (!
I.getType()->isAggregateType())
2164 B.SetInsertPoint(&
I);
2165 TrackConstants =
false;
2170 B.CreateIntrinsic(Intrinsic::spv_load, {
I.getOperand(0)->getType()},
2171 {
I.getPointerOperand(),
B.getInt16(Flags),
2172 B.getInt32(
I.getAlign().value())});
2173 replaceMemInstrUses(&
I, NewI,
B);
2177Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &
I) {
2181 B.SetInsertPoint(&
I);
2182 TrackConstants =
false;
2186 auto *PtrOp =
I.getPointerOperand();
2188 if (
I.getValueOperand()->getType()->isAggregateType()) {
2196 "Unexpected argument of aggregate type, should be spv_extractv!");
2200 auto *NewI =
B.CreateIntrinsic(
2201 Intrinsic::spv_store, {
I.getValueOperand()->getType(), PtrOp->
getType()},
2202 {
I.getValueOperand(), PtrOp,
B.getInt16(Flags),
2203 B.getInt32(
I.getAlign().value())});
2205 I.eraseFromParent();
2209Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &
I) {
2210 Value *ArraySize =
nullptr;
2211 if (
I.isArrayAllocation()) {
2214 SPIRV::Extension::SPV_INTEL_variable_length_array))
2216 "array allocation: this instruction requires the following "
2217 "SPIR-V extension: SPV_INTEL_variable_length_array",
2219 ArraySize =
I.getArraySize();
2222 B.SetInsertPoint(&
I);
2223 TrackConstants =
false;
2224 Type *PtrTy =
I.getType();
2227 ?
B.CreateIntrinsic(Intrinsic::spv_alloca_array,
2228 {PtrTy, ArraySize->
getType()},
2229 {ArraySize,
B.getInt32(
I.getAlign().value())})
2230 :
B.CreateIntrinsic(
Intrinsic::spv_alloca, {PtrTy},
2231 {
B.getInt32(
I.getAlign().value())});
2232 replaceAllUsesWithAndErase(
B, &
I, NewI);
2236Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &
I) {
2237 assert(
I.getType()->isAggregateType() &&
"Aggregate result is expected");
2239 B.SetInsertPoint(&
I);
2241 Args.push_back(
B.getInt32(
2242 static_cast<uint32_t
>(
getMemScope(
I.getContext(),
I.getSyncScopeID()))));
2243 Args.push_back(
B.getInt32(
2245 Args.push_back(
B.getInt32(
2247 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_cmpxchg,
2248 {
I.getPointerOperand()->getType()}, {
Args});
2249 replaceMemInstrUses(&
I, NewI,
B);
2253Instruction *SPIRVEmitIntrinsics::visitUnreachableInst(UnreachableInst &
I) {
2255 B.SetInsertPoint(&
I);
2256 B.CreateIntrinsic(Intrinsic::spv_unreachable, {});
2265 static const StringSet<> ArtificialGlobals{
"llvm.global.annotations",
2266 "llvm.compiler.used",
"llvm.used"};
2271 auto &UserFunctions = GVUsers.getTransitiveUserFunctions(GV);
2272 if (UserFunctions.contains(
F))
2277 if (!UserFunctions.empty())
2282 const Module &M = *
F->getParent();
2283 const Function &FirstDefinition = *M.getFunctionDefs().
begin();
2284 return F == &FirstDefinition;
2287Value *SPIRVEmitIntrinsics::buildSpvUndefComposite(
Type *AggrTy,
2289 SmallVector<Value *, 4> Elems;
2291 Type *ElemTy = ArrTy->getElementType();
2292 auto *UI =
B.CreateIntrinsic(Intrinsic::spv_undef, {});
2294 AggrConstTypes[UI] = ElemTy;
2295 Elems.
assign(ArrTy->getNumElements(), UI);
2298 DenseMap<Type *, Instruction *> UndefByType;
2299 for (
unsigned I = 0;
I < StructTy->getNumElements(); ++
I) {
2301 auto &
Entry = UndefByType[ElemTy];
2303 Entry =
B.CreateIntrinsic(Intrinsic::spv_undef, {});
2305 AggrConstTypes[
Entry] = ElemTy;
2310 auto *Composite =
B.CreateIntrinsic(Intrinsic::spv_const_composite,
2311 {
B.getInt32Ty()}, Elems);
2313 AggrConstTypes[Composite] = AggrTy;
2317void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV,
2328 deduceElementTypeHelper(&GV,
false);
2330 Value *InitOp = Init;
2332 InitOp = buildSpvUndefComposite(Init->
getType(),
B);
2335 auto *InitInst =
B.CreateIntrinsic(Intrinsic::spv_init_global,
2337 InitInst->setArgOperand(1, InitOp);
2340 B.CreateIntrinsic(Intrinsic::spv_unref_global, GV.
getType(), &GV);
2346bool SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *
I,
2348 bool UnknownElemTypeI8) {
2354 if (
Type *ElemTy = deduceElementType(
I, UnknownElemTypeI8)) {
2361void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *
I,
2364 static StringMap<unsigned> ResTypeWellKnown = {
2365 {
"async_work_group_copy", WellKnownTypes::Event},
2366 {
"async_work_group_strided_copy", WellKnownTypes::Event},
2367 {
"__spirv_GroupAsyncCopy", WellKnownTypes::Event}};
2371 bool IsKnown =
false;
2376 std::string DemangledName =
2379 if (DemangledName.length() > 0)
2381 SPIRV::lookupBuiltinNameHelper(DemangledName, &DecorationId);
2382 auto ResIt = ResTypeWellKnown.
find(DemangledName);
2383 if (ResIt != ResTypeWellKnown.
end()) {
2386 switch (ResIt->second) {
2387 case WellKnownTypes::Event:
2394 switch (DecorationId) {
2397 case FPDecorationId::SAT:
2400 case FPDecorationId::RTE:
2402 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTE,
B);
2404 case FPDecorationId::RTZ:
2406 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTZ,
B);
2408 case FPDecorationId::RTP:
2410 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTP,
B);
2412 case FPDecorationId::RTN:
2414 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTN,
B);
2420 Type *Ty =
I->getType();
2423 Type *TypeToAssign = Ty;
2425 if (
II->getIntrinsicID() == Intrinsic::spv_const_composite ||
2426 II->getIntrinsicID() == Intrinsic::spv_undef) {
2427 auto It = AggrConstTypes.
find(
II);
2428 if (It == AggrConstTypes.
end())
2430 TypeToAssign = It->second;
2432 }
else if (
auto It = AggrConstTypes.
find(
I); It != AggrConstTypes.
end())
2433 TypeToAssign = It->second;
2437 for (
const auto &
Op :
I->operands()) {
2444 Type *OpTy =
Op->getType();
2446 CallInst *AssignCI =
2451 Type *OpTy =
Op->getType();
2466 CallInst *AssignCI =
2476bool SPIRVEmitIntrinsics::shouldTryToAddMemAliasingDecoration(
2477 Instruction *Inst) {
2479 if (!STI->
canUseExtension(SPIRV::Extension::SPV_INTEL_memory_access_aliasing))
2493 const std::string
Prefix =
"__spirv_Atomic";
2494 const bool IsAtomic =
Name.find(Prefix) == 0;
2502void SPIRVEmitIntrinsics::insertSpirvDecorations(Instruction *
I,
2504 if (MDNode *MD =
I->getMetadata(
"spirv.Decorations")) {
2506 B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {
I->getType()},
2511 auto processMemAliasingDecoration = [&](
unsigned Kind) {
2512 if (MDNode *AliasListMD =
I->getMetadata(Kind)) {
2513 if (shouldTryToAddMemAliasingDecoration(
I)) {
2514 uint32_t Dec =
Kind == LLVMContext::MD_alias_scope
2515 ? SPIRV::Decoration::AliasScopeINTEL
2516 : SPIRV::Decoration::NoAliasINTEL;
2518 I, ConstantInt::get(
B.getInt32Ty(), Dec),
2521 B.CreateIntrinsic(Intrinsic::spv_assign_aliasing_decoration,
2522 {
I->getType()}, {
Args});
2526 processMemAliasingDecoration(LLVMContext::MD_alias_scope);
2527 processMemAliasingDecoration(LLVMContext::MD_noalias);
2530 if (MDNode *MD =
I->getMetadata(LLVMContext::MD_fpmath)) {
2532 bool AllowFPMaxError =
2534 if (!AllowFPMaxError)
2538 B.CreateIntrinsic(Intrinsic::spv_assign_fpmaxerror_decoration,
2547 &FPFastMathDefaultInfoMap,
2549 auto it = FPFastMathDefaultInfoMap.
find(
F);
2550 if (it != FPFastMathDefaultInfoMap.
end())
2558 SPIRV::FPFastMathMode::None);
2560 SPIRV::FPFastMathMode::None);
2562 SPIRV::FPFastMathMode::None);
2563 return FPFastMathDefaultInfoMap[
F] = std::move(FPFastMathDefaultInfoVec);
2569 size_t BitWidth = Ty->getScalarSizeInBits();
2573 assert(Index >= 0 && Index < 3 &&
2574 "Expected FPFastMathDefaultInfo for half, float, or double");
2575 assert(FPFastMathDefaultInfoVec.
size() == 3 &&
2576 "Expected FPFastMathDefaultInfoVec to have exactly 3 elements");
2577 return FPFastMathDefaultInfoVec[Index];
2580void SPIRVEmitIntrinsics::insertConstantsForFPFastMathDefault(
Module &M) {
2582 if (!
ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2))
2591 auto Node =
M.getNamedMetadata(
"spirv.ExecutionMode");
2593 if (!
M.getNamedMetadata(
"opencl.enable.FP_CONTRACT")) {
2601 ConstantInt::get(Type::getInt32Ty(
M.getContext()), 0);
2604 [[maybe_unused]] GlobalVariable *GV =
2605 new GlobalVariable(M,
2606 Type::getInt32Ty(
M.getContext()),
2620 DenseMap<Function *, SPIRV::FPFastMathDefaultInfoVector>
2621 FPFastMathDefaultInfoMap;
2623 for (
unsigned i = 0; i <
Node->getNumOperands(); i++) {
2632 if (EM == SPIRV::ExecutionMode::FPFastMathDefault) {
2634 "Expected 4 operands for FPFastMathDefault");
2640 SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec =
2642 SPIRV::FPFastMathDefaultInfo &
Info =
2645 Info.FPFastMathDefault =
true;
2646 }
else if (EM == SPIRV::ExecutionMode::ContractionOff) {
2648 "Expected no operands for ContractionOff");
2652 SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec =
2654 for (SPIRV::FPFastMathDefaultInfo &Info : FPFastMathDefaultInfoVec) {
2655 Info.ContractionOff =
true;
2657 }
else if (EM == SPIRV::ExecutionMode::SignedZeroInfNanPreserve) {
2659 "Expected 1 operand for SignedZeroInfNanPreserve");
2660 unsigned TargetWidth =
2665 SPIRV::FPFastMathDefaultInfoVector &FPFastMathDefaultInfoVec =
2669 assert(Index >= 0 && Index < 3 &&
2670 "Expected FPFastMathDefaultInfo for half, float, or double");
2671 assert(FPFastMathDefaultInfoVec.
size() == 3 &&
2672 "Expected FPFastMathDefaultInfoVec to have exactly 3 elements");
2673 FPFastMathDefaultInfoVec[
Index].SignedZeroInfNanPreserve =
true;
2677 std::unordered_map<unsigned, GlobalVariable *> GlobalVars;
2678 for (
auto &[Func, FPFastMathDefaultInfoVec] : FPFastMathDefaultInfoMap) {
2679 if (FPFastMathDefaultInfoVec.
empty())
2682 for (
const SPIRV::FPFastMathDefaultInfo &Info : FPFastMathDefaultInfoVec) {
2683 assert(
Info.Ty &&
"Expected target type for FPFastMathDefaultInfo");
2686 if (Flags == SPIRV::FPFastMathMode::None && !
Info.ContractionOff &&
2687 !
Info.SignedZeroInfNanPreserve && !
Info.FPFastMathDefault)
2691 if (
Info.ContractionOff && (Flags & SPIRV::FPFastMathMode::AllowContract))
2693 "and AllowContract");
2695 if (
Info.SignedZeroInfNanPreserve &&
2697 (SPIRV::FPFastMathMode::NotNaN | SPIRV::FPFastMathMode::NotInf |
2698 SPIRV::FPFastMathMode::NSZ))) {
2699 if (
Info.FPFastMathDefault)
2701 "SignedZeroInfNanPreserve but at least one of "
2702 "NotNaN/NotInf/NSZ is enabled.");
2705 if ((Flags & SPIRV::FPFastMathMode::AllowTransform) &&
2706 !((Flags & SPIRV::FPFastMathMode::AllowReassoc) &&
2707 (Flags & SPIRV::FPFastMathMode::AllowContract))) {
2709 "AllowTransform requires AllowReassoc and "
2710 "AllowContract to be set.");
2713 auto it = GlobalVars.find(Flags);
2714 GlobalVariable *GV =
nullptr;
2715 if (it != GlobalVars.end()) {
2721 ConstantInt::get(Type::getInt32Ty(
M.getContext()), Flags);
2724 GV =
new GlobalVariable(M,
2725 Type::getInt32Ty(
M.getContext()),
2730 GlobalVars[
Flags] = GV;
2736void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *
I,
2739 bool IsConstComposite =
2740 II &&
II->getIntrinsicID() == Intrinsic::spv_const_composite;
2741 if (IsConstComposite && TrackConstants) {
2743 auto t = AggrConsts.
find(
I);
2747 {
II->getType(),
II->getType()}, t->second,
I, {},
B);
2749 NewOp->setArgOperand(0,
I);
2752 for (
const auto &
Op :
I->operands()) {
2756 unsigned OpNo =
Op.getOperandNo();
2757 if (
II && ((
II->getIntrinsicID() == Intrinsic::spv_gep && OpNo == 0) ||
2758 (!
II->isBundleOperand(OpNo) &&
2759 II->paramHasAttr(OpNo, Attribute::ImmArg))))
2763 IsPhi ?
B.SetInsertPointPastAllocas(
I->getParent()->getParent())
2764 :
B.SetInsertPoint(
I);
2767 Type *OpTy =
Op->getType();
2775 {OpTy, OpTyVal->
getType()},
Op, OpTyVal, {},
B);
2777 if (!IsConstComposite &&
isPointerTy(OpTy) && OpElemTy !=
nullptr &&
2778 OpElemTy != IntegerType::getInt8Ty(
I->getContext())) {
2780 SmallVector<Value *, 2>
Args = {
2783 CallInst *PtrCasted =
2784 B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
2789 I->setOperand(OpNo, NewOp);
2791 if (Named.insert(
I).second)
2795Type *SPIRVEmitIntrinsics::deduceFunParamElementType(Function *
F,
2797 std::unordered_set<Function *> FVisited;
2798 return deduceFunParamElementType(
F,
OpIdx, FVisited);
2801Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
2802 Function *
F,
unsigned OpIdx, std::unordered_set<Function *> &FVisited) {
2804 if (!FVisited.insert(
F).second)
2807 std::unordered_set<Value *> Visited;
2810 for (User *U :
F->users()) {
2822 if (
Type *Ty = deduceElementTypeHelper(OpArg, Visited,
false))
2825 for (User *OpU : OpArg->
users()) {
2827 if (!Inst || Inst == CI)
2830 if (
Type *Ty = deduceElementTypeHelper(Inst, Visited,
false))
2837 if (FVisited.find(OuterF) != FVisited.end())
2839 for (
unsigned i = 0; i < OuterF->
arg_size(); ++i) {
2840 if (OuterF->
getArg(i) == OpArg) {
2841 Lookup.push_back(std::make_pair(OuterF, i));
2848 for (
auto &Pair :
Lookup) {
2849 if (
Type *Ty = deduceFunParamElementType(Pair.first, Pair.second, FVisited))
2856void SPIRVEmitIntrinsics::processParamTypesByFunHeader(Function *
F,
2858 B.SetInsertPointPastAllocas(
F);
2872 for (User *U :
F->users()) {
2888 for (User *U : Arg->
users()) {
2892 CI->
getParent()->getParent() == CurrF) {
2894 deduceOperandElementTypeFunctionPointer(CI,
Ops, ElemTy,
false);
2905void SPIRVEmitIntrinsics::processParamTypes(Function *
F,
IRBuilder<> &
B) {
2906 B.SetInsertPointPastAllocas(
F);
2912 if (!ElemTy && (ElemTy = deduceFunParamElementType(
F,
OpIdx)) !=
nullptr) {
2914 DenseSet<std::pair<Value *, Value *>> VisitedSubst;
2916 propagateElemType(Arg, IntegerType::getInt8Ty(
F->getContext()),
2928 bool IsNewFTy =
false;
2944bool SPIRVEmitIntrinsics::processFunctionPointers(
Module &M) {
2947 if (
F.isIntrinsic())
2949 if (
F.isDeclaration()) {
2950 for (User *U :
F.users()) {
2963 for (User *U :
F.users()) {
2965 if (!
II ||
II->arg_size() != 3 ||
II->getOperand(0) != &
F)
2967 if (
II->getIntrinsicID() == Intrinsic::spv_assign_ptr_type ||
2968 II->getIntrinsicID() == Intrinsic::spv_ptrcast) {
2975 if (Worklist.
empty())
2978 LLVMContext &Ctx =
M.getContext();
2983 for (Function *
F : Worklist) {
2985 for (
const auto &Arg :
F->args())
2987 IRB.CreateCall(
F, Args);
2989 IRB.CreateRetVoid();
2995void SPIRVEmitIntrinsics::applyDemangledPtrArgTypes(
IRBuilder<> &
B) {
2996 DenseMap<Function *, CallInst *> Ptrcasts;
2997 for (
auto It : FDeclPtrTys) {
2999 for (
auto *U :
F->users()) {
3004 for (
auto [Idx, ElemTy] : It.second) {
3012 B.SetInsertPointPastAllocas(Arg->
getParent());
3016 }
else if (isaGEP(Param)) {
3017 replaceUsesOfWithSpvPtrcast(Param,
normalizeType(ElemTy), CI,
3026 .getFirstNonPHIOrDbgOrAlloca());
3047SPIRVEmitIntrinsics::simplifyZeroLengthArrayGepInst(GetElementPtrInst *
GEP) {
3054 Type *SrcTy =
GEP->getSourceElementType();
3055 SmallVector<Value *, 8> Indices(
GEP->indices());
3057 if (ArrTy && ArrTy->getNumElements() == 0 &&
match(Indices[0],
m_Zero())) {
3058 Indices.erase(Indices.begin());
3059 SrcTy = ArrTy->getElementType();
3061 GEP->getNoWrapFlags(),
"",
3062 GEP->getIterator());
3067void SPIRVEmitIntrinsics::emitUnstructuredLoopControls(Function &
F,
3074 if (
ST->canUseExtension(
3075 SPIRV::Extension::SPV_INTEL_unstructured_loop_controls)) {
3076 for (BasicBlock &BB :
F) {
3078 MDNode *LoopMD =
Term->getMetadata(LLVMContext::MD_loop);
3084 unsigned LC =
Ops[0];
3085 if (LC == SPIRV::LoopControl::None)
3089 B.SetInsertPoint(Term);
3090 SmallVector<Value *, 4> IntrArgs;
3091 for (
unsigned Op :
Ops)
3093 B.CreateIntrinsic(Intrinsic::spv_loop_control_intel, IntrArgs);
3100 DominatorTree DT(
F);
3105 for (Loop *L : LI.getLoopsInPreorder()) {
3116 if (LoopControlOps[0] == SPIRV::LoopControl::None)
3120 B.SetInsertPoint(Header->getTerminator());
3123 SmallVector<Value *, 4>
Args = {MergeAddress, ContinueAddress};
3124 for (
unsigned Imm : LoopControlOps)
3125 Args.emplace_back(
B.getInt32(Imm));
3126 B.CreateIntrinsic(Intrinsic::spv_loop_merge, {
Args});
3130bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
3131 if (
Func.isDeclaration())
3135 GR =
ST.getSPIRVGlobalRegistry();
3139 ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
3144 AggrConstTypes.
clear();
3146 DeletedInstrs.
clear();
3148 processParamTypesByFunHeader(CurrF,
B);
3152 SmallPtrSet<Instruction *, 4> DeadInsts;
3157 if ((!
GEP && !SGEP) || GR->findDeducedElementType(&
I))
3161 GR->addDeducedElementType(SGEP,
3166 GetElementPtrInst *NewGEP = simplifyZeroLengthArrayGepInst(
GEP);
3168 GEP->replaceAllUsesWith(NewGEP);
3172 if (
Type *GepTy = getGEPType(
GEP))
3176 for (
auto *
I : DeadInsts) {
3177 assert(
I->use_empty() &&
"Dead instruction should not have any uses left");
3178 I->eraseFromParent();
3188 Type *ElTy =
SI->getValueOperand()->getType();
3193 B.SetInsertPoint(&
Func.getEntryBlock(),
Func.getEntryBlock().begin());
3194 for (
auto &GV :
Func.getParent()->globals())
3195 processGlobalValue(GV,
B);
3197 preprocessUndefs(
B);
3198 simplifyNullAddrSpaceCasts();
3199 preprocessCompositeConstants(
B);
3201 for (BasicBlock &BB : Func)
3202 for (PHINode &Phi : BB.
phis())
3203 if (
Phi.getType()->isAggregateType()) {
3204 AggrConstTypes[&
Phi] =
Phi.getType();
3205 Phi.mutateType(
B.getInt32Ty());
3208 preprocessBoolVectorBitcasts(Func);
3212 applyDemangledPtrArgTypes(
B);
3215 for (
auto &
I : Worklist) {
3217 if (isConvergenceIntrinsic(
I))
3220 bool Postpone = insertAssignPtrTypeIntrs(
I,
B,
false);
3222 insertAssignTypeIntrs(
I,
B);
3223 insertPtrCastOrAssignTypeInstr(
I,
B);
3227 if (Postpone && !GR->findAssignPtrTypeInstr(
I))
3228 insertAssignPtrTypeIntrs(
I,
B,
true);
3231 useRoundingMode(FPI,
B);
3236 SmallPtrSet<Instruction *, 4> IncompleteRets;
3238 deduceOperandElementType(&
I, &IncompleteRets);
3242 for (BasicBlock &BB : Func)
3243 for (PHINode &Phi : BB.
phis())
3245 deduceOperandElementType(&Phi,
nullptr);
3247 for (
auto *
I : Worklist) {
3248 if (DeletedInstrs.
count(
I))
3250 TrackConstants =
true;
3260 if (isConvergenceIntrinsic(
I))
3264 processInstrAfterVisit(
I,
B);
3267 emitUnstructuredLoopControls(Func,
B);
3273bool SPIRVEmitIntrinsics::postprocessTypes(
Module &M) {
3274 if (!GR || TodoTypeSz == 0)
3277 unsigned SzTodo = TodoTypeSz;
3278 DenseMap<Value *, SmallPtrSet<Value *, 4>> ToProcess;
3283 CallInst *AssignCI = GR->findAssignPtrTypeInstr(
Op);
3284 Type *KnownTy = GR->findDeducedElementType(
Op);
3285 if (!KnownTy || !AssignCI)
3291 std::unordered_set<Value *> Visited;
3292 if (
Type *ElemTy = deduceElementTypeHelper(
Op, Visited,
false,
true)) {
3293 if (ElemTy != KnownTy) {
3294 DenseSet<std::pair<Value *, Value *>> VisitedSubst;
3295 propagateElemType(CI, ElemTy, VisitedSubst);
3302 if (
Op->hasUseList()) {
3303 for (User *U :
Op->users()) {
3310 if (TodoTypeSz == 0)
3315 SmallPtrSet<Instruction *, 4> IncompleteRets;
3317 auto It = ToProcess.
find(&
I);
3318 if (It == ToProcess.
end())
3320 It->second.remove_if([
this](
Value *V) {
return !isTodoType(V); });
3321 if (It->second.size() == 0)
3323 deduceOperandElementType(&
I, &IncompleteRets, &It->second,
true);
3324 if (TodoTypeSz == 0)
3329 return SzTodo > TodoTypeSz;
3333void SPIRVEmitIntrinsics::parseFunDeclarations(
Module &M) {
3335 if (!
F.isDeclaration() ||
F.isIntrinsic())
3339 if (DemangledName.empty())
3343 auto [Grp, Opcode, ExtNo] = SPIRV::mapBuiltinToOpcode(
3344 DemangledName,
ST.getPreferredInstructionSet());
3345 if (Opcode != SPIRV::OpGroupAsyncCopy)
3348 SmallVector<unsigned> Idxs;
3357 LLVMContext &Ctx =
F.getContext();
3359 SPIRV::parseBuiltinTypeStr(TypeStrs, DemangledName, Ctx);
3360 if (!TypeStrs.
size())
3363 for (
unsigned Idx : Idxs) {
3364 if (Idx >= TypeStrs.
size())
3367 SPIRV::parseBuiltinCallArgumentType(TypeStrs[Idx].trim(), Ctx))
3370 FDeclPtrTys[&
F].push_back(std::make_pair(Idx, ElemTy));
3375bool SPIRVEmitIntrinsics::processMaskedMemIntrinsic(IntrinsicInst &
I) {
3376 const SPIRVSubtarget &
ST = TM.
getSubtarget<SPIRVSubtarget>(*
I.getFunction());
3378 if (
I.getIntrinsicID() == Intrinsic::masked_gather) {
3379 if (!
ST.canUseExtension(
3380 SPIRV::Extension::SPV_INTEL_masked_gather_scatter)) {
3381 I.getContext().emitError(
3382 &
I,
"llvm.masked.gather requires SPV_INTEL_masked_gather_scatter "
3386 I.eraseFromParent();
3392 Value *Ptrs =
I.getArgOperand(0);
3394 Value *Passthru =
I.getArgOperand(2);
3397 uint32_t Alignment =
I.getParamAlign(0).valueOrOne().value();
3399 SmallVector<Value *, 4>
Args = {Ptrs,
B.getInt32(Alignment),
Mask,
3404 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_masked_gather, Types, Args);
3406 I.eraseFromParent();
3410 if (
I.getIntrinsicID() == Intrinsic::masked_scatter) {
3411 if (!
ST.canUseExtension(
3412 SPIRV::Extension::SPV_INTEL_masked_gather_scatter)) {
3413 I.getContext().emitError(
3414 &
I,
"llvm.masked.scatter requires SPV_INTEL_masked_gather_scatter "
3417 I.eraseFromParent();
3423 Value *Values =
I.getArgOperand(0);
3424 Value *Ptrs =
I.getArgOperand(1);
3429 uint32_t Alignment =
I.getParamAlign(1).valueOrOne().value();
3431 SmallVector<Value *, 4>
Args = {Values, Ptrs,
B.getInt32(Alignment),
Mask};
3435 B.CreateIntrinsic(Intrinsic::spv_masked_scatter, Types, Args);
3436 I.eraseFromParent();
3447void SPIRVEmitIntrinsics::preprocessBoolVectorBitcasts(Function &
F) {
3448 struct BoolVecBitcast {
3450 FixedVectorType *BoolVecTy;
3454 auto getAsBoolVec = [](
Type *Ty) -> FixedVectorType * {
3456 return (VTy && VTy->getElementType()->
isIntegerTy(1)) ? VTy :
nullptr;
3464 if (
auto *BVTy = getAsBoolVec(BC->getSrcTy()))
3466 else if (
auto *BVTy = getAsBoolVec(BC->getDestTy()))
3470 for (
auto &[BC, BoolVecTy, SrcIsBoolVec] : ToReplace) {
3472 Value *Src = BC->getOperand(0);
3473 unsigned BoolVecN = BoolVecTy->getNumElements();
3475 Type *IntTy =
B.getIntNTy(BoolVecN);
3481 IntVal = ConstantInt::get(IntTy, 0);
3482 for (
unsigned I = 0;
I < BoolVecN; ++
I) {
3483 Value *Elem =
B.CreateExtractElement(Src,
B.getInt32(
I));
3484 Value *Ext =
B.CreateZExt(Elem, IntTy);
3486 Ext =
B.CreateShl(Ext, ConstantInt::get(IntTy,
I));
3487 IntVal =
B.CreateOr(IntVal, Ext);
3493 if (!Src->getType()->isIntegerTy())
3494 IntVal =
B.CreateBitCast(Src, IntTy);
3499 if (!SrcIsBoolVec) {
3502 for (
unsigned I = 0;
I < BoolVecN; ++
I) {
3505 Value *
Cmp =
B.CreateICmpNE(
And, ConstantInt::get(IntTy, 0));
3506 Result =
B.CreateInsertElement(Result, Cmp,
B.getInt32(
I));
3512 if (!BC->getDestTy()->isIntegerTy())
3513 Result =
B.CreateBitCast(IntVal, BC->getDestTy());
3516 BC->replaceAllUsesWith(Result);
3517 BC->eraseFromParent();
3521bool SPIRVEmitIntrinsics::convertMaskedMemIntrinsics(
Module &M) {
3525 if (!
F.isIntrinsic())
3528 if (IID != Intrinsic::masked_gather && IID != Intrinsic::masked_scatter)
3533 Changed |= processMaskedMemIntrinsic(*
II);
3537 F.eraseFromParent();
3543bool SPIRVEmitIntrinsics::runOnModule(
Module &M) {
3546 Changed |= convertMaskedMemIntrinsics(M);
3548 parseFunDeclarations(M);
3549 insertConstantsForFPFastMathDefault(M);
3560 if (!
F.isDeclaration() && !
F.isIntrinsic()) {
3562 processParamTypes(&
F,
B);
3566 CanTodoType =
false;
3567 Changed |= postprocessTypes(M);
3570 Changed |= processFunctionPointers(M);
3577 SPIRVEmitIntrinsics Legacy(TM);
3578 if (Legacy.runOnModule(M))
3584 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 ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
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.
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)
IntrinsicID_match m_Intrinsic()
Match intrinsic calls like this: m_Intrinsic<Intrinsic::fabs>(m_Value(X))
auto m_Value()
Match an arbitrary value and ignore it.
auto m_AnyIntrinsic()
Matches any intrinsic call and ignore it.
is_zero m_Zero()
Match any null constant or a vector with all elements equal to 0.
@ CE
Windows NT (Windows on ARM)
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)