24#include "llvm/IR/IntrinsicsSPIRV.h"
28#include <unordered_set>
52#define GET_BuiltinGroup_DECL
53#include "SPIRVGenTables.inc"
66class SPIRVEmitIntrinsics
68 public InstVisitor<SPIRVEmitIntrinsics, Instruction *> {
72 bool TrackConstants =
true;
73 bool HaveFunPtrs =
false;
77 SPIRV::InstructionSet::InstructionSet InstrSet;
83 bool CanTodoType =
true;
84 unsigned TodoTypeSz = 0;
86 void insertTodoType(
Value *
Op) {
88 if (CanTodoType && !isa<GetElementPtrInst>(
Op)) {
95 auto It = TodoType.
find(
Op);
96 if (It != TodoType.
end() && It->second) {
102 if (isa<GetElementPtrInst>(
Op))
104 auto It = TodoType.
find(
Op);
105 return It != TodoType.
end() && It->second;
109 std::unordered_set<Instruction *> TypeValidated;
112 enum WellKnownTypes { Event };
115 Type *deduceElementType(
Value *
I,
bool UnknownElemTypeI8);
116 Type *deduceElementTypeHelper(
Value *
I,
bool UnknownElemTypeI8);
117 Type *deduceElementTypeHelper(
Value *
I, std::unordered_set<Value *> &Visited,
118 bool UnknownElemTypeI8,
119 bool IgnoreKnownType =
false);
120 Type *deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
121 bool UnknownElemTypeI8);
122 Type *deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
123 std::unordered_set<Value *> &Visited,
124 bool UnknownElemTypeI8);
126 std::unordered_set<Value *> &Visited,
127 bool UnknownElemTypeI8);
129 bool UnknownElemTypeI8);
132 Type *deduceNestedTypeHelper(
User *U,
bool UnknownElemTypeI8);
134 std::unordered_set<Value *> &Visited,
135 bool UnknownElemTypeI8);
141 bool IsPostprocessing =
false);
150 Args.push_back(Arg2);
151 Args.push_back(buildMD(Arg));
152 for (
auto *Imm : Imms)
154 return B.CreateIntrinsic(IntrID, {
Types},
Args);
157 Type *reconstructType(
Value *
Op,
bool UnknownElemTypeI8,
158 bool IsPostprocessing);
167 bool UnknownElemTypeI8);
172 Type *ExpectedElementType,
173 unsigned OperandToReplace,
180 Type *deduceFunParamElementType(
Function *
F,
unsigned OpIdx);
182 std::unordered_set<Function *> &FVisited);
184 bool deduceOperandElementTypeCalledFunction(
187 void deduceOperandElementTypeFunctionPointer(
189 Type *&KnownElemTy,
bool IsPostprocessing);
190 bool deduceOperandElementTypeFunctionRet(
199 DenseSet<std::pair<Value *, Value *>> &VisitedSubst);
202 DenseSet<std::pair<Value *, Value *>> &VisitedSubst);
203 void propagateElemTypeRec(
Value *
Op,
Type *PtrElemTy,
Type *CastElemTy,
204 DenseSet<std::pair<Value *, Value *>> &VisitedSubst,
205 std::unordered_set<Value *> &Visited,
215 bool postprocessTypes(
Module &M);
216 bool processFunctionPointers(
Module &M);
217 void parseFunDeclarations(
Module &M);
254 const auto *
II = dyn_cast<IntrinsicInst>(
I);
258 return II->getIntrinsicID() == Intrinsic::experimental_convergence_entry ||
259 II->getIntrinsicID() == Intrinsic::experimental_convergence_loop ||
260 II->getIntrinsicID() == Intrinsic::experimental_convergence_anchor;
263bool expectIgnoredInIRTranslation(
const Instruction *
I) {
264 const auto *
II = dyn_cast<IntrinsicInst>(
I);
267 return II->getIntrinsicID() == Intrinsic::invariant_start;
270bool allowEmitFakeUse(
const Value *Arg) {
273 if (dyn_cast<AtomicCmpXchgInst>(Arg) || dyn_cast<InsertValueInst>(Arg) ||
274 dyn_cast<UndefValue>(Arg))
276 if (
const auto *LI = dyn_cast<LoadInst>(Arg))
277 if (LI->getType()->isAggregateType())
284char SPIRVEmitIntrinsics::ID = 0;
290 return isa<IntrinsicInst>(
I) &&
291 cast<IntrinsicInst>(
I)->getIntrinsicID() == Intrinsic::spv_assign_type;
295 return isa<StoreInst>(
I) || isa<LoadInst>(
I) || isa<InsertValueInst>(
I) ||
296 isa<ExtractValueInst>(
I) || isa<AtomicCmpXchgInst>(
I);
300 return isa<ConstantArray>(V) || isa<ConstantStruct>(V) ||
301 isa<ConstantDataArray>(V) ||
302 (isa<ConstantAggregateZero>(V) && !V->getType()->isVectorTy());
307 B.SetInsertPoint(
I->getParent()->getFirstNonPHIOrDbgOrAlloca());
313 B.SetCurrentDebugLocation(
I->getDebugLoc());
314 if (
I->getType()->isVoidTy())
315 B.SetInsertPoint(
I->getNextNode());
317 B.SetInsertPoint(*
I->getInsertionPointAfterDef());
323 switch (
Intr->getIntrinsicID()) {
324 case Intrinsic::invariant_start:
325 case Intrinsic::invariant_end:
333 if (
I->getType()->isTokenTy())
335 "does not support token type",
340 if (!
I->hasName() ||
I->getType()->isAggregateType() ||
341 expectIgnoredInIRTranslation(
I))
345 std::vector<Value *> Args = {
I};
347 B.CreateIntrinsic(Intrinsic::spv_assign_name, {
I->getType()}, Args);
350void SPIRVEmitIntrinsics::replaceAllUsesWith(
Value *Src,
Value *Dest,
352 Src->replaceAllUsesWith(Dest);
357 if (isTodoType(Src)) {
360 insertTodoType(Dest);
364void SPIRVEmitIntrinsics::replaceAllUsesWithAndErase(
IRBuilder<> &
B,
369 std::string
Name = Src->hasName() ? Src->getName().str() :
"";
370 Src->eraseFromParent();
380 isa<Argument>(SI->getValueOperand());
397Type *SPIRVEmitIntrinsics::reconstructType(
Value *
Op,
bool UnknownElemTypeI8,
398 bool IsPostprocessing) {
400 if (
auto *OpI = dyn_cast<Instruction>(
Op))
411 return cast<ConstantAsMetadata>(MD->
getMetadata())->getType();
413 if (UnknownElemTypeI8) {
414 if (!IsPostprocessing)
427 allowEmitFakeUse(Arg)) {
432 B.CreateIntrinsic(Intrinsic::spv_value_md, {},
434 AssignCI =
B.CreateIntrinsic(Intrinsic::fake_use, {}, {Arg});
436 AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type, {Arg->
getType()},
446 if (AssignPtrTyCI ==
nullptr ||
447 AssignPtrTyCI->
getParent()->getParent() != CurrF) {
448 AssignPtrTyCI = buildIntrWithMD(
449 Intrinsic::spv_assign_ptr_type, {Arg->
getType()}, OfType, Arg,
455 updateAssignType(AssignPtrTyCI, Arg, OfType);
459void SPIRVEmitIntrinsics::updateAssignType(
CallInst *AssignCI,
Value *Arg,
463 Intrinsic::spv_assign_ptr_type)
475 if (
auto *OpI = dyn_cast<Instruction>(
Op)) {
479 }
else if (
auto *OpA = dyn_cast<Argument>(
Op)) {
480 B.SetInsertPointPastAllocas(OpA->getParent());
483 B.SetInsertPoint(
F->getEntryBlock().getFirstNonPHIOrDbgOrAlloca());
485 Type *OpTy =
Op->getType();
490 B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
491 buildAssignPtr(
B, ElemTy, PtrCasted);
495void SPIRVEmitIntrinsics::replaceUsesOfWithSpvPtrcast(
500 auto It = Ptrcasts.
find(
F);
501 if (It == Ptrcasts.
end()) {
502 PtrCastedI = buildSpvPtrcast(
F,
Op, ElemTy);
503 Ptrcasts[
F] = PtrCastedI;
505 PtrCastedI = It->second;
507 I->replaceUsesOfWith(
Op, PtrCastedI);
510void SPIRVEmitIntrinsics::propagateElemType(
512 DenseSet<std::pair<Value *, Value *>> &VisitedSubst) {
515 for (
auto *U :
Users) {
518 if (!VisitedSubst.insert(std::make_pair(U,
Op)).second)
523 if (isa<GetElementPtrInst>(UI) ||
524 TypeValidated.find(UI) != TypeValidated.end())
525 replaceUsesOfWithSpvPtrcast(
Op, ElemTy, UI, Ptrcasts);
529void SPIRVEmitIntrinsics::propagateElemTypeRec(
531 DenseSet<std::pair<Value *, Value *>> &VisitedSubst) {
532 std::unordered_set<Value *> Visited;
534 propagateElemTypeRec(
Op, PtrElemTy, CastElemTy, VisitedSubst, Visited,
538void SPIRVEmitIntrinsics::propagateElemTypeRec(
540 DenseSet<std::pair<Value *, Value *>> &VisitedSubst,
541 std::unordered_set<Value *> &Visited,
543 if (!Visited.insert(
Op).second)
546 for (
auto *U :
Users) {
549 if (!VisitedSubst.insert(std::make_pair(U,
Op)).second)
554 if (isa<GetElementPtrInst>(UI) ||
555 TypeValidated.find(UI) != TypeValidated.end())
556 replaceUsesOfWithSpvPtrcast(
Op, CastElemTy, UI, Ptrcasts);
564SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
Type *ValueTy,
Value *Operand,
565 bool UnknownElemTypeI8) {
566 std::unordered_set<Value *> Visited;
567 return deduceElementTypeByValueDeep(ValueTy, Operand, Visited,
571Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
572 Type *ValueTy,
Value *Operand, std::unordered_set<Value *> &Visited,
573 bool UnknownElemTypeI8) {
576 if (
auto *PtrTy = dyn_cast<PointerType>(Ty)) {
578 deduceElementTypeHelper(Operand, Visited, UnknownElemTypeI8))
581 Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited,
589Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
590 Value *
Op, std::unordered_set<Value *> &Visited,
bool UnknownElemTypeI8) {
602 for (
User *OpU :
Op->users()) {
603 if (
Instruction *Inst = dyn_cast<Instruction>(OpU)) {
604 if (
Type *Ty = deduceElementTypeHelper(Inst, Visited, UnknownElemTypeI8))
616 Function *CalledF,
unsigned OpIdx) {
617 if ((DemangledName.
starts_with(
"__spirv_ocl_printf(") ||
620 return IntegerType::getInt8Ty(CalledF->
getContext());
626Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
Value *
I,
627 bool UnknownElemTypeI8) {
628 std::unordered_set<Value *> Visited;
629 return deduceElementTypeHelper(
I, Visited, UnknownElemTypeI8);
632void SPIRVEmitIntrinsics::maybeAssignPtrType(
Type *&Ty,
Value *
Op,
Type *RefTy,
633 bool UnknownElemTypeI8) {
635 if (!UnknownElemTypeI8)
642Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
643 Value *
I, std::unordered_set<Value *> &Visited,
bool UnknownElemTypeI8,
644 bool IgnoreKnownType) {
650 if (!IgnoreKnownType)
655 if (!Visited.insert(
I).second)
661 if (
auto *
Ref = dyn_cast<AllocaInst>(
I)) {
662 maybeAssignPtrType(Ty,
I,
Ref->getAllocatedType(), UnknownElemTypeI8);
663 }
else if (
auto *
Ref = dyn_cast<GetElementPtrInst>(
I)) {
667 Ty =
Ref->getSourceElementType();
671 Ty =
Ref->getResultElementType();
673 }
else if (
auto *
Ref = dyn_cast<LoadInst>(
I)) {
677 KnownTy =
Op->getType();
679 maybeAssignPtrType(Ty,
I, ElemTy, UnknownElemTypeI8);
680 }
else if (
auto *
Ref = dyn_cast<GlobalValue>(
I)) {
681 Ty = deduceElementTypeByValueDeep(
683 Ref->getNumOperands() > 0 ?
Ref->getOperand(0) :
nullptr, Visited,
685 }
else if (
auto *
Ref = dyn_cast<AddrSpaceCastInst>(
I)) {
686 Type *RefTy = deduceElementTypeHelper(
Ref->getPointerOperand(), Visited,
688 maybeAssignPtrType(Ty,
I, RefTy, UnknownElemTypeI8);
689 }
else if (
auto *
Ref = dyn_cast<BitCastInst>(
I)) {
690 if (
Type *Src =
Ref->getSrcTy(), *Dest =
Ref->getDestTy();
692 Ty = deduceElementTypeHelper(
Ref->getOperand(0), Visited,
694 }
else if (
auto *
Ref = dyn_cast<AtomicCmpXchgInst>(
I)) {
697 Ty = deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8);
698 }
else if (
auto *
Ref = dyn_cast<AtomicRMWInst>(
I)) {
701 Ty = deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8);
702 }
else if (
auto *
Ref = dyn_cast<PHINode>(
I)) {
703 Type *BestTy =
nullptr;
706 for (
int i =
Ref->getNumIncomingValues() - 1; i >= 0; --i) {
707 Ty = deduceElementTypeByUsersDeep(
Ref->getIncomingValue(i), Visited,
714 if (It.first->second > MaxN) {
715 MaxN = It.first->second;
722 }
else if (
auto *
Ref = dyn_cast<SelectInst>(
I)) {
723 for (
Value *
Op : {
Ref->getTrueValue(),
Ref->getFalseValue()}) {
724 Ty = deduceElementTypeByUsersDeep(
Op, Visited, UnknownElemTypeI8);
728 }
else if (
auto *CI = dyn_cast<CallInst>(
I)) {
733 {
"__spirv_GenericCastToPtr_ToGlobal", 0},
734 {
"__spirv_GenericCastToPtr_ToLocal", 0},
735 {
"__spirv_GenericCastToPtr_ToPrivate", 0},
736 {
"__spirv_GenericCastToPtrExplicit_ToGlobal", 0},
737 {
"__spirv_GenericCastToPtrExplicit_ToLocal", 0},
738 {
"__spirv_GenericCastToPtrExplicit_ToPrivate", 0}};
741 std::string DemangledName =
743 if (DemangledName.length() > 0)
745 auto AsArgIt = ResTypeByArg.
find(DemangledName);
746 if (AsArgIt != ResTypeByArg.
end())
747 Ty = deduceElementTypeHelper(CI->
getArgOperand(AsArgIt->second),
748 Visited, UnknownElemTypeI8);
755 if (Ty && !IgnoreKnownType) {
766Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
User *U,
767 bool UnknownElemTypeI8) {
768 std::unordered_set<Value *> Visited;
769 return deduceNestedTypeHelper(U,
U->getType(), Visited, UnknownElemTypeI8);
772Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
773 User *U,
Type *OrigTy, std::unordered_set<Value *> &Visited,
774 bool UnknownElemTypeI8) {
783 if (!Visited.insert(U).second)
786 if (dyn_cast<StructType>(OrigTy)) {
789 for (
unsigned i = 0; i <
U->getNumOperands(); ++i) {
791 Type *OpTy =
Op->getType();
794 if (
auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
796 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
799 Ty = deduceNestedTypeHelper(dyn_cast<User>(
Op), OpTy, Visited,
804 Change |= Ty != OpTy;
811 }
else if (
auto *ArrTy = dyn_cast<ArrayType>(OrigTy)) {
812 if (
Value *
Op =
U->getNumOperands() > 0 ?
U->getOperand(0) :
nullptr) {
813 Type *OpTy = ArrTy->getElementType();
815 if (
auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
817 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
820 Ty = deduceNestedTypeHelper(dyn_cast<User>(
Op), OpTy, Visited,
824 Type *NewTy = ArrayType::get(Ty, ArrTy->getNumElements());
829 }
else if (
auto *VecTy = dyn_cast<VectorType>(OrigTy)) {
830 if (
Value *
Op =
U->getNumOperands() > 0 ?
U->getOperand(0) :
nullptr) {
831 Type *OpTy = VecTy->getElementType();
833 if (
auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
835 deduceElementTypeHelper(
Op, Visited, UnknownElemTypeI8))
838 Ty = deduceNestedTypeHelper(dyn_cast<User>(
Op), OpTy, Visited,
842 Type *NewTy = VectorType::get(Ty, VecTy->getElementCount());
852Type *SPIRVEmitIntrinsics::deduceElementType(
Value *
I,
bool UnknownElemTypeI8) {
853 if (
Type *Ty = deduceElementTypeHelper(
I, UnknownElemTypeI8))
855 if (!UnknownElemTypeI8)
858 return IntegerType::getInt8Ty(
I->getContext());
862 Value *PointerOperand) {
866 auto *PtrTy = dyn_cast<PointerType>(
I->getType());
876bool SPIRVEmitIntrinsics::deduceOperandElementTypeCalledFunction(
878 Type *&KnownElemTy) {
882 std::string DemangledName =
884 if (DemangledName.length() > 0 &&
886 auto [Grp, Opcode, ExtNo] =
888 if (Opcode == SPIRV::OpGroupAsyncCopy) {
889 for (
unsigned i = 0, PtrCnt = 0; i < CI->
arg_size() && PtrCnt < 2; ++i) {
895 KnownElemTy = ElemTy;
896 Ops.push_back(std::make_pair(
Op, i));
898 }
else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) {
905 case SPIRV::OpAtomicLoad:
906 case SPIRV::OpAtomicCompareExchangeWeak:
907 case SPIRV::OpAtomicCompareExchange:
908 case SPIRV::OpAtomicExchange:
909 case SPIRV::OpAtomicIAdd:
910 case SPIRV::OpAtomicISub:
911 case SPIRV::OpAtomicOr:
912 case SPIRV::OpAtomicXor:
913 case SPIRV::OpAtomicAnd:
914 case SPIRV::OpAtomicUMin:
915 case SPIRV::OpAtomicUMax:
916 case SPIRV::OpAtomicSMin:
917 case SPIRV::OpAtomicSMax: {
921 Ops.push_back(std::make_pair(
Op, 0));
930void SPIRVEmitIntrinsics::deduceOperandElementTypeFunctionPointer(
932 Type *&KnownElemTy,
bool IsPostprocessing) {
936 Ops.push_back(std::make_pair(
Op, std::numeric_limits<unsigned>::max()));
938 bool IsNewFTy =
false, IsUncomplete =
false;
966 if (!IsPostprocessing && IsUncomplete)
969 IsNewFTy ? FunctionType::get(
RetTy, ArgTys, FTy->isVarArg()) : FTy;
972bool SPIRVEmitIntrinsics::deduceOperandElementTypeFunctionRet(
986 for (
User *U :
F->users()) {
987 CallInst *CI = dyn_cast<CallInst>(U);
993 propagateElemType(CI, PrevElemTy, VisitedSubst);
1003 for (
Instruction *UncompleteRetI : *UncompleteRets)
1004 deduceOperandElementType(UncompleteRetI,
nullptr, AskOps,
1006 }
else if (UncompleteRets) {
1009 TypeValidated.insert(
I);
1017void SPIRVEmitIntrinsics::deduceOperandElementType(
1021 Type *KnownElemTy =
nullptr;
1022 bool Uncomplete =
false;
1024 if (
auto *
Ref = dyn_cast<PHINode>(
I)) {
1028 Uncomplete = isTodoType(
I);
1029 for (
unsigned i = 0; i <
Ref->getNumIncomingValues(); i++) {
1034 }
else if (
auto *
Ref = dyn_cast<AddrSpaceCastInst>(
I)) {
1038 Uncomplete = isTodoType(
I);
1039 Ops.
push_back(std::make_pair(
Ref->getPointerOperand(), 0));
1040 }
else if (
auto *
Ref = dyn_cast<BitCastInst>(
I)) {
1046 Uncomplete = isTodoType(
I);
1048 }
else if (
auto *
Ref = dyn_cast<GetElementPtrInst>(
I)) {
1051 KnownElemTy =
Ref->getSourceElementType();
1054 }
else if (
auto *
Ref = dyn_cast<LoadInst>(
I)) {
1055 KnownElemTy =
I->getType();
1063 }
else if (
auto *
Ref = dyn_cast<StoreInst>(
I)) {
1065 reconstructType(
Ref->getValueOperand(),
false, IsPostprocessing)))
1072 }
else if (
auto *
Ref = dyn_cast<AtomicCmpXchgInst>(
I)) {
1078 }
else if (
auto *
Ref = dyn_cast<AtomicRMWInst>(
I)) {
1084 }
else if (
auto *
Ref = dyn_cast<SelectInst>(
I)) {
1088 Uncomplete = isTodoType(
I);
1089 for (
unsigned i = 0; i <
Ref->getNumOperands(); i++) {
1094 }
else if (
auto *
Ref = dyn_cast<ReturnInst>(
I)) {
1100 if (deduceOperandElementTypeFunctionRet(
I, UncompleteRets, AskOps,
1101 IsPostprocessing, KnownElemTy,
Op,
1104 Uncomplete = isTodoType(CurrF);
1106 }
else if (
auto *
Ref = dyn_cast<ICmpInst>(
I)) {
1114 KnownElemTy = ElemTy0;
1115 Uncomplete = isTodoType(Op0);
1117 }
else if (ElemTy1) {
1118 KnownElemTy = ElemTy1;
1119 Uncomplete = isTodoType(Op1);
1122 }
else if (
CallInst *CI = dyn_cast<CallInst>(
I)) {
1124 deduceOperandElementTypeCalledFunction(CI, Ops, KnownElemTy);
1125 else if (HaveFunPtrs)
1126 deduceOperandElementTypeFunctionPointer(CI, Ops, KnownElemTy,
1131 if (!KnownElemTy || Ops.
size() == 0)
1136 for (
auto &OpIt : Ops) {
1138 if (
Op->use_empty())
1142 Type *AskTy =
nullptr;
1144 if (IsPostprocessing && AskOps) {
1150 if (Ty == KnownElemTy)
1153 Type *OpTy =
Op->getType();
1160 else if (!IsPostprocessing)
1164 if (AssignCI ==
nullptr) {
1168 buildIntrWithMD(Intrinsic::spv_assign_ptr_type, {OpTy}, OpTyVal,
Op,
1172 updateAssignType(AssignCI,
Op, OpTyVal);
1174 std::make_pair(
I,
Op)};
1175 propagateElemTypeRec(
Op, KnownElemTy, PrevElemTy, VisitedSubst);
1180 buildSpvPtrcast(
I->getParent()->getParent(),
Op, KnownElemTy);
1181 if (OpIt.second == std::numeric_limits<unsigned>::max())
1182 dyn_cast<CallInst>(
I)->setCalledOperand(PtrCastI);
1184 I->setOperand(OpIt.second, PtrCastI);
1187 TypeValidated.insert(
I);
1190void SPIRVEmitIntrinsics::replaceMemInstrUses(
Instruction *Old,
1195 if (isAssignTypeInstr(U)) {
1196 B.SetInsertPoint(U);
1199 B.CreateIntrinsic(Intrinsic::spv_assign_type, {
New->getType()},
Args);
1201 U->eraseFromParent();
1204 U->replaceUsesOfWith(Old, New);
1212void SPIRVEmitIntrinsics::preprocessUndefs(
IRBuilder<> &
B) {
1213 std::queue<Instruction *> Worklist;
1217 while (!Worklist.empty()) {
1219 bool BPrepared =
false;
1222 for (
auto &
Op :
I->operands()) {
1223 auto *AggrUndef = dyn_cast<UndefValue>(
Op);
1224 if (!AggrUndef || !
Op->getType()->isAggregateType())
1231 auto *IntrUndef =
B.CreateIntrinsic(Intrinsic::spv_undef, {}, {});
1232 Worklist.push(IntrUndef);
1233 I->replaceUsesOfWith(
Op, IntrUndef);
1234 AggrConsts[IntrUndef] = AggrUndef;
1235 AggrConstTypes[IntrUndef] = AggrUndef->getType();
1240void SPIRVEmitIntrinsics::preprocessCompositeConstants(
IRBuilder<> &
B) {
1241 std::queue<Instruction *> Worklist;
1245 while (!Worklist.empty()) {
1246 auto *
I = Worklist.front();
1247 bool IsPhi = isa<PHINode>(
I), BPrepared =
false;
1249 bool KeepInst =
false;
1250 for (
const auto &
Op :
I->operands()) {
1252 Type *ResTy =
nullptr;
1253 if (
auto *COp = dyn_cast<ConstantVector>(
Op)) {
1254 AggrConst = cast<Constant>(COp);
1255 ResTy = COp->getType();
1256 }
else if (
auto *COp = dyn_cast<ConstantArray>(
Op)) {
1257 AggrConst = cast<Constant>(COp);
1258 ResTy =
B.getInt32Ty();
1259 }
else if (
auto *COp = dyn_cast<ConstantStruct>(
Op)) {
1260 AggrConst = cast<Constant>(COp);
1261 ResTy =
B.getInt32Ty();
1262 }
else if (
auto *COp = dyn_cast<ConstantDataArray>(
Op)) {
1263 AggrConst = cast<Constant>(COp);
1264 ResTy =
B.getInt32Ty();
1265 }
else if (
auto *COp = dyn_cast<ConstantAggregateZero>(
Op)) {
1266 AggrConst = cast<Constant>(COp);
1267 ResTy =
Op->getType()->isVectorTy() ? COp->getType() :
B.getInt32Ty();
1271 if (
auto *COp = dyn_cast<ConstantDataSequential>(
Op))
1272 for (
unsigned i = 0; i < COp->getNumElements(); ++i)
1273 Args.push_back(COp->getElementAsConstant(i));
1275 for (
auto &COp : AggrConst->
operands())
1276 Args.push_back(COp);
1278 IsPhi ?
B.SetInsertPointPastAllocas(
I->getParent()->getParent())
1279 :
B.SetInsertPoint(
I);
1283 B.CreateIntrinsic(Intrinsic::spv_const_composite, {ResTy}, {
Args});
1285 I->replaceUsesOfWith(
Op, CI);
1287 AggrConsts[CI] = AggrConst;
1288 AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst,
false);
1300 B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {
I->getType()},
1305 unsigned RoundingModeDeco,
1312 ConstantInt::get(Int32Ty, SPIRV::Decoration::FPRoundingMode)),
1321 MDNode *SaturatedConversionNode =
1323 Int32Ty, SPIRV::Decoration::SaturatedConversion))});
1328 if (!
Call.isInlineAsm())
1339 for (
unsigned OpIdx = 0; OpIdx <
Call.arg_size(); OpIdx++)
1340 Args.push_back(
Call.getArgOperand(OpIdx));
1343 B.SetInsertPoint(&Call);
1344 B.CreateIntrinsic(Intrinsic::spv_inline_asm, {}, {
Args});
1352 if (!
RM.has_value())
1354 unsigned RoundingModeDeco = std::numeric_limits<unsigned>::max();
1355 switch (
RM.value()) {
1359 case RoundingMode::NearestTiesToEven:
1360 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTE;
1362 case RoundingMode::TowardNegative:
1363 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTN;
1365 case RoundingMode::TowardPositive:
1366 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTP;
1368 case RoundingMode::TowardZero:
1369 RoundingModeDeco = SPIRV::FPRoundingMode::FPRoundingMode::RTZ;
1371 case RoundingMode::Dynamic:
1372 case RoundingMode::NearestTiesToAway:
1376 if (RoundingModeDeco == std::numeric_limits<unsigned>::max())
1385 B.SetInsertPoint(&
I);
1388 for (
auto &
Op :
I.operands()) {
1389 if (
Op.get()->getType()->isSized()) {
1391 }
else if (
BasicBlock *BB = dyn_cast<BasicBlock>(
Op.get())) {
1398 CallInst *NewI =
B.CreateIntrinsic(Intrinsic::spv_switch,
1403 I.eraseFromParent();
1406 B.SetInsertPoint(ParentBB);
1417 B.SetInsertPoint(&
I);
1420 Args.push_back(
B.getInt1(
I.isInBounds()));
1421 for (
auto &
Op :
I.operands())
1423 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_gep, {
Types}, {
Args});
1424 replaceAllUsesWithAndErase(
B, &
I, NewI);
1430 B.SetInsertPoint(&
I);
1439 I.eraseFromParent();
1445 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_bitcast, {
Types}, {
Args});
1446 replaceAllUsesWithAndErase(
B, &
I, NewI);
1450void SPIRVEmitIntrinsics::insertAssignPtrTypeTargetExt(
1452 Type *VTy =
V->getType();
1457 if (ElemTy != AssignedType)
1462 buildAssignType(
B, AssignedType, V);
1467 dyn_cast<ConstantAsMetadata>(
1468 cast<MetadataAsValue>(AssignCI->
getOperand(1))->getMetadata())
1470 if (CurrentType == AssignedType)
1477 " for value " +
V->getName(),
1485void SPIRVEmitIntrinsics::replacePointerOperandWithPtrCast(
1488 TypeValidated.insert(
I);
1491 Type *PointerElemTy = deduceElementTypeHelper(Pointer,
false);
1492 if (PointerElemTy == ExpectedElementType ||
1500 bool FirstPtrCastOrAssignPtrType =
true;
1506 auto *
II = dyn_cast<IntrinsicInst>(
User);
1508 (
II->getIntrinsicID() != Intrinsic::spv_assign_ptr_type &&
1509 II->getIntrinsicID() != Intrinsic::spv_ptrcast) ||
1510 II->getOperand(0) != Pointer)
1515 FirstPtrCastOrAssignPtrType =
false;
1516 if (
II->getOperand(1) != VMD ||
1517 dyn_cast<ConstantInt>(
II->getOperand(2))->getSExtValue() !=
1523 if (
II->getIntrinsicID() != Intrinsic::spv_ptrcast)
1528 if (
II->getParent() !=
I->getParent())
1531 I->setOperand(OperandToReplace,
II);
1535 if (isa<Instruction>(Pointer) || isa<Argument>(Pointer)) {
1536 if (FirstPtrCastOrAssignPtrType) {
1539 buildAssignPtr(
B, ExpectedElementType, Pointer);
1541 }
else if (isTodoType(Pointer)) {
1542 eraseTodoType(Pointer);
1543 if (!isa<CallInst>(Pointer) && !isa<GetElementPtrInst>(Pointer)) {
1550 std::make_pair(
I, Pointer)};
1551 updateAssignType(AssignCI, Pointer, ExpectedElementVal);
1552 propagateElemType(Pointer, PrevElemTy, VisitedSubst);
1554 buildAssignPtr(
B, ExpectedElementType, Pointer);
1564 auto *PtrCastI =
B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
1565 I->setOperand(OperandToReplace, PtrCastI);
1567 buildAssignPtr(
B, ExpectedElementType, PtrCastI);
1570void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(
Instruction *
I,
1575 replacePointerOperandWithPtrCast(
1576 I,
SI->getValueOperand(), IntegerType::getInt8Ty(CurrF->
getContext()),
1582 Type *OpTy =
Op->getType();
1583 if (
auto *OpI = dyn_cast<Instruction>(
Op))
1585 if (OpTy ==
Op->getType())
1586 OpTy = deduceElementTypeByValueDeep(OpTy,
Op,
false);
1587 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 1,
B);
1590 if (
LoadInst *LI = dyn_cast<LoadInst>(
I)) {
1592 Type *OpTy = LI->getType();
1593 if (
auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
1597 Type *NewOpTy = OpTy;
1598 OpTy = deduceElementTypeByValueDeep(OpTy, LI,
false);
1599 if (OpTy == NewOpTy)
1600 insertTodoType(Pointer);
1603 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 0,
B);
1608 Type *OpTy = GEPI->getSourceElementType();
1609 replacePointerOperandWithPtrCast(
I, Pointer, OpTy, 0,
B);
1611 insertTodoType(Pointer);
1623 std::string DemangledName =
1627 bool HaveTypes =
false;
1628 for (
unsigned OpIdx = 0; OpIdx < CalledF->
arg_size(); ++OpIdx) {
1646 if (
Instruction *Inst = dyn_cast<Instruction>(U)) {
1647 if ((ElemTy = deduceElementTypeHelper(Inst,
false)) !=
nullptr)
1653 HaveTypes |= ElemTy !=
nullptr;
1658 if (DemangledName.empty() && !HaveTypes)
1661 for (
unsigned OpIdx = 0; OpIdx < CI->
arg_size(); OpIdx++) {
1667 if (!isa<Instruction>(ArgOperand) && !isa<Argument>(ArgOperand)) {
1676 Type *ExpectedType =
1677 OpIdx < CalledArgTys.
size() ? CalledArgTys[OpIdx] :
nullptr;
1678 if (!ExpectedType && !DemangledName.empty())
1680 DemangledName, OpIdx,
I->getContext());
1681 if (!ExpectedType || ExpectedType->
isVoidTy())
1686 insertAssignPtrTypeTargetExt(cast<TargetExtType>(ExpectedType),
1689 replacePointerOperandWithPtrCast(CI, ArgOperand, ExpectedType, OpIdx,
B);
1695 I.getOperand(1)->getType(),
1696 I.getOperand(2)->getType()};
1698 B.SetInsertPoint(&
I);
1700 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_insertelt, {
Types}, {
Args});
1701 replaceAllUsesWithAndErase(
B, &
I, NewI);
1708 B.SetInsertPoint(&
I);
1710 I.getIndexOperand()->getType()};
1712 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_extractelt, {
Types}, {
Args});
1713 replaceAllUsesWithAndErase(
B, &
I, NewI);
1719 B.SetInsertPoint(&
I);
1722 for (
auto &
Op :
I.operands())
1723 if (isa<UndefValue>(
Op))
1727 for (
auto &
Op :
I.indices())
1728 Args.push_back(
B.getInt32(
Op));
1730 B.CreateIntrinsic(Intrinsic::spv_insertv, {
Types}, {
Args});
1731 replaceMemInstrUses(&
I, NewI,
B);
1736 if (
I.getAggregateOperand()->getType()->isAggregateType())
1739 B.SetInsertPoint(&
I);
1741 for (
auto &
Op :
I.operands())
1743 for (
auto &
Op :
I.indices())
1744 Args.push_back(
B.getInt32(
Op));
1746 B.CreateIntrinsic(Intrinsic::spv_extractv, {
I.getType()}, {
Args});
1747 replaceAllUsesWithAndErase(
B, &
I, NewI);
1752 if (!
I.getType()->isAggregateType())
1755 B.SetInsertPoint(&
I);
1756 TrackConstants =
false;
1757 const auto *TLI =
TM->getSubtargetImpl()->getTargetLowering();
1761 B.CreateIntrinsic(Intrinsic::spv_load, {
I.getOperand(0)->
getType()},
1762 {
I.getPointerOperand(),
B.getInt16(Flags),
1763 B.getInt8(
I.getAlign().value())});
1764 replaceMemInstrUses(&
I, NewI,
B);
1772 B.SetInsertPoint(&
I);
1773 TrackConstants =
false;
1774 const auto *TLI =
TM->getSubtargetImpl()->getTargetLowering();
1777 auto *PtrOp =
I.getPointerOperand();
1778 auto *NewI =
B.CreateIntrinsic(
1779 Intrinsic::spv_store, {
I.getValueOperand()->
getType(), PtrOp->getType()},
1780 {
I.getValueOperand(), PtrOp,
B.getInt16(Flags),
1781 B.getInt8(
I.getAlign().value())});
1782 I.eraseFromParent();
1787 Value *ArraySize =
nullptr;
1788 if (
I.isArrayAllocation()) {
1791 SPIRV::Extension::SPV_INTEL_variable_length_array))
1793 "array allocation: this instruction requires the following "
1794 "SPIR-V extension: SPV_INTEL_variable_length_array",
1796 ArraySize =
I.getArraySize();
1799 B.SetInsertPoint(&
I);
1800 TrackConstants =
false;
1801 Type *PtrTy =
I.getType();
1804 ?
B.CreateIntrinsic(Intrinsic::spv_alloca_array,
1805 {PtrTy, ArraySize->
getType()},
1806 {ArraySize,
B.getInt8(
I.getAlign().value())})
1807 :
B.CreateIntrinsic(Intrinsic::spv_alloca, {PtrTy},
1808 {B.getInt8(I.getAlign().value())});
1809 replaceAllUsesWithAndErase(
B, &
I, NewI);
1814 assert(
I.getType()->isAggregateType() &&
"Aggregate result is expected");
1816 B.SetInsertPoint(&
I);
1818 for (
auto &
Op :
I.operands())
1820 Args.push_back(
B.getInt32(
1822 Args.push_back(
B.getInt32(
1824 Args.push_back(
B.getInt32(
1826 auto *NewI =
B.CreateIntrinsic(Intrinsic::spv_cmpxchg,
1828 replaceMemInstrUses(&
I, NewI,
B);
1834 B.SetInsertPoint(&
I);
1835 B.CreateIntrinsic(Intrinsic::spv_unreachable, {}, {});
1842 if (GV.
getName() ==
"llvm.global.annotations")
1848 deduceElementTypeHelper(&GV,
false);
1852 auto *InitInst =
B.CreateIntrinsic(Intrinsic::spv_init_global,
1854 InitInst->setArgOperand(1,
Init);
1858 B.CreateIntrinsic(Intrinsic::spv_unref_global, GV.
getType(), &GV);
1864bool SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(
Instruction *
I,
1866 bool UnknownElemTypeI8) {
1872 if (
Type *ElemTy = deduceElementType(
I, UnknownElemTypeI8)) {
1873 buildAssignPtr(
B, ElemTy,
I);
1879void SPIRVEmitIntrinsics::insertAssignTypeIntrs(
Instruction *
I,
1883 {
"async_work_group_copy", WellKnownTypes::Event},
1884 {
"async_work_group_strided_copy", WellKnownTypes::Event},
1885 {
"__spirv_GroupAsyncCopy", WellKnownTypes::Event}};
1889 bool IsKnown =
false;
1890 if (
auto *CI = dyn_cast<CallInst>(
I)) {
1894 std::string DemangledName =
1897 if (DemangledName.length() > 0)
1900 auto ResIt = ResTypeWellKnown.
find(DemangledName);
1901 if (ResIt != ResTypeWellKnown.
end()) {
1904 switch (ResIt->second) {
1905 case WellKnownTypes::Event:
1912 switch (DecorationId) {
1915 case FPDecorationId::SAT:
1918 case FPDecorationId::RTE:
1920 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTE,
B);
1922 case FPDecorationId::RTZ:
1924 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTZ,
B);
1926 case FPDecorationId::RTP:
1928 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTP,
B);
1930 case FPDecorationId::RTN:
1932 CI, SPIRV::FPRoundingMode::FPRoundingMode::RTN,
B);
1938 Type *Ty =
I->getType();
1941 Type *TypeToAssign = Ty;
1942 if (
auto *
II = dyn_cast<IntrinsicInst>(
I)) {
1943 if (
II->getIntrinsicID() == Intrinsic::spv_const_composite ||
1944 II->getIntrinsicID() == Intrinsic::spv_undef) {
1945 auto It = AggrConstTypes.
find(
II);
1946 if (It == AggrConstTypes.
end())
1948 TypeToAssign = It->second;
1952 buildAssignType(
B, TypeToAssign,
I);
1954 for (
const auto &
Op :
I->operands()) {
1955 if (isa<ConstantPointerNull>(
Op) || isa<UndefValue>(
Op) ||
1957 (isa<ConstantExpr>(
Op) && isa<GEPOperator>(
Op))) {
1959 Type *OpTy =
Op->getType();
1962 buildIntrWithMD(Intrinsic::spv_assign_type, {
B.getInt32Ty()},
Op,
1965 }
else if (!isa<Instruction>(
Op)) {
1966 Type *OpTy =
Op->getType();
1969 buildAssignPtr(
B, OpTyElem,
Op);
1972 buildAssignPtr(
B, ElemTy ? ElemTy : deduceElementType(
Op,
true),
Op);
1974 CallInst *AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type,
1975 {OpTy},
Op,
Op, {},
B);
1983void SPIRVEmitIntrinsics::insertSpirvDecorations(
Instruction *
I,
1985 if (
MDNode *MD =
I->getMetadata(
"spirv.Decorations")) {
1987 B.CreateIntrinsic(Intrinsic::spv_assign_decoration, {
I->getType()},
1992void SPIRVEmitIntrinsics::processInstrAfterVisit(
Instruction *
I,
1994 auto *
II = dyn_cast<IntrinsicInst>(
I);
1995 bool IsConstComposite =
1996 II &&
II->getIntrinsicID() == Intrinsic::spv_const_composite;
1997 if (IsConstComposite && TrackConstants) {
1999 auto t = AggrConsts.
find(
I);
2002 buildIntrWithMD(Intrinsic::spv_track_constant,
2003 {
II->getType(),
II->getType()}, t->second,
I, {},
B);
2005 NewOp->setArgOperand(0,
I);
2007 bool IsPhi = isa<PHINode>(
I), BPrepared =
false;
2008 for (
const auto &
Op :
I->operands()) {
2009 if (isa<PHINode>(
I) || isa<SwitchInst>(
I))
2010 TrackConstants =
false;
2011 if ((isa<ConstantData>(
Op) || isa<ConstantExpr>(
Op)) && TrackConstants) {
2012 unsigned OpNo =
Op.getOperandNo();
2013 if (
II && ((
II->getIntrinsicID() == Intrinsic::spv_gep && OpNo == 0) ||
2014 (
II->paramHasAttr(OpNo, Attribute::ImmArg))))
2017 IsPhi ?
B.SetInsertPointPastAllocas(
I->getParent()->getParent())
2018 :
B.SetInsertPoint(
I);
2021 Type *OpTy =
Op->getType();
2026 buildIntrWithMD(Intrinsic::spv_track_constant,
2027 {OpTy, OpTyVal->
getType()},
Op, OpTyVal, {},
B);
2028 Type *OpElemTy =
nullptr;
2031 OpElemTy != IntegerType::getInt8Ty(
I->getContext())) {
2032 buildAssignPtr(
B, IntegerType::getInt8Ty(
I->getContext()), NewOp);
2038 B.CreateIntrinsic(Intrinsic::spv_ptrcast, {
Types},
Args);
2039 buildAssignPtr(
B, OpElemTy, PtrCasted);
2042 I->setOperand(OpNo, NewOp);
2048Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
Function *
F,
2050 std::unordered_set<Function *> FVisited;
2051 return deduceFunParamElementType(
F, OpIdx, FVisited);
2054Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
2055 Function *
F,
unsigned OpIdx, std::unordered_set<Function *> &FVisited) {
2057 if (!FVisited.insert(
F).second)
2060 std::unordered_set<Value *> Visited;
2063 for (
User *U :
F->users()) {
2064 CallInst *CI = dyn_cast<CallInst>(U);
2065 if (!CI || OpIdx >= CI->
arg_size())
2075 if (
Type *Ty = deduceElementTypeHelper(OpArg, Visited,
false))
2080 if (!Inst || Inst == CI)
2083 if (
Type *Ty = deduceElementTypeHelper(Inst, Visited,
false))
2090 if (FVisited.find(OuterF) != FVisited.end())
2092 for (
unsigned i = 0; i < OuterF->
arg_size(); ++i) {
2093 if (OuterF->
getArg(i) == OpArg) {
2094 Lookup.push_back(std::make_pair(OuterF, i));
2101 for (
auto &Pair :
Lookup) {
2102 if (
Type *Ty = deduceFunParamElementType(Pair.first, Pair.second, FVisited))
2109void SPIRVEmitIntrinsics::processParamTypesByFunHeader(
Function *
F,
2111 B.SetInsertPointPastAllocas(
F);
2112 for (
unsigned OpIdx = 0; OpIdx <
F->arg_size(); ++OpIdx) {
2121 buildAssignPtr(
B, ElemTy, Arg);
2125 for (
User *U :
F->users()) {
2126 CallInst *CI = dyn_cast<CallInst>(U);
2127 if (!CI || OpIdx >= CI->
arg_size())
2137 buildAssignPtr(
B, ElemTy, Arg);
2142 CallInst *CI = dyn_cast<CallInst>(U);
2145 CI->
getParent()->getParent() == CurrF) {
2147 deduceOperandElementTypeFunctionPointer(CI, Ops, ElemTy,
false);
2149 buildAssignPtr(
B, ElemTy, Arg);
2159 B.SetInsertPointPastAllocas(
F);
2160 for (
unsigned OpIdx = 0; OpIdx <
F->arg_size(); ++OpIdx) {
2165 if (!ElemTy && (ElemTy = deduceFunParamElementType(
F, OpIdx)) !=
nullptr) {
2169 propagateElemType(Arg, IntegerType::getInt8Ty(
F->getContext()),
2172 buildAssignPtr(
B, ElemTy, Arg);
2181 bool IsNewFTy =
false;
2193 ? FunctionType::get(FTy->getReturnType(), ArgTys, FTy->isVarArg())
2197bool SPIRVEmitIntrinsics::processFunctionPointers(
Module &M) {
2200 if (
F.isIntrinsic())
2202 if (
F.isDeclaration()) {
2203 for (
User *U :
F.users()) {
2204 CallInst *CI = dyn_cast<CallInst>(U);
2216 for (
User *U :
F.users()) {
2218 if (!
II ||
II->arg_size() != 3 ||
II->getOperand(0) != &
F)
2220 if (
II->getIntrinsicID() == Intrinsic::spv_assign_ptr_type ||
2221 II->getIntrinsicID() == Intrinsic::spv_ptrcast) {
2228 if (Worklist.
empty())
2234 "cannot allocate a name for the internal service function");
2245 for (
const auto &Arg :
F->args())
2247 IRB.CreateCall(
F, Args);
2249 IRB.CreateRetVoid();
2255void SPIRVEmitIntrinsics::applyDemangledPtrArgTypes(
IRBuilder<> &
B) {
2256 for (
auto It : FDeclPtrTys) {
2258 for (
auto *U :
F->users()) {
2259 CallInst *CI = dyn_cast<CallInst>(U);
2263 for (
auto [
Idx, ElemTy] : It.second) {
2269 if (
Argument *Arg = dyn_cast<Argument>(Param)) {
2271 B.SetInsertPointPastAllocas(Arg->
getParent());
2273 buildAssignPtr(
B, ElemTy, Arg);
2275 }
else if (isa<Instruction>(Param)) {
2282 .getFirstNonPHIOrDbgOrAlloca());
2283 buildAssignPtr(
B, ElemTy, Param);
2301bool SPIRVEmitIntrinsics::runOnFunction(
Function &Func) {
2302 if (
Func.isDeclaration())
2306 GR =
ST.getSPIRVGlobalRegistry();
2307 InstrSet =
ST.isOpenCLEnv() ? SPIRV::InstructionSet::OpenCL_std
2308 : SPIRV::InstructionSet::GLSL_std_450;
2312 ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
2317 AggrConstTypes.
clear();
2320 processParamTypesByFunHeader(CurrF,
B);
2328 Type *ElTy =
SI->getValueOperand()->getType();
2333 B.SetInsertPoint(&
Func.getEntryBlock(),
Func.getEntryBlock().begin());
2334 for (
auto &GV :
Func.getParent()->globals())
2335 processGlobalValue(GV,
B);
2337 preprocessUndefs(
B);
2338 preprocessCompositeConstants(
B);
2343 applyDemangledPtrArgTypes(
B);
2346 for (
auto &
I : Worklist) {
2348 if (isConvergenceIntrinsic(
I))
2351 bool Postpone = insertAssignPtrTypeIntrs(
I,
B,
false);
2353 insertAssignTypeIntrs(
I,
B);
2354 insertPtrCastOrAssignTypeInstr(
I,
B);
2358 if (Postpone && !GR->findAssignPtrTypeInstr(
I))
2359 insertAssignPtrTypeIntrs(
I,
B,
true);
2361 if (
auto *FPI = dyn_cast<ConstrainedFPIntrinsic>(
I))
2362 useRoundingMode(FPI,
B);
2369 deduceOperandElementType(&
I, &UncompleteRets);
2376 deduceOperandElementType(&Phi,
nullptr);
2378 for (
auto *
I : Worklist) {
2379 TrackConstants =
true;
2380 if (!
I->getType()->isVoidTy() || isa<StoreInst>(
I))
2389 if (isConvergenceIntrinsic(
I))
2392 processInstrAfterVisit(
I,
B);
2399bool SPIRVEmitIntrinsics::postprocessTypes(
Module &M) {
2400 if (!GR || TodoTypeSz == 0)
2403 unsigned SzTodo = TodoTypeSz;
2407 if (!
Enabled || isa<GetElementPtrInst>(
Op))
2409 CallInst *AssignCI = GR->findAssignPtrTypeInstr(
Op);
2410 Type *KnownTy = GR->findDeducedElementType(
Op);
2411 if (!KnownTy || !AssignCI)
2415 if (
auto *CI = dyn_cast<Instruction>(
Op)) {
2417 std::unordered_set<Value *> Visited;
2418 if (
Type *ElemTy = deduceElementTypeHelper(
Op, Visited,
false,
true)) {
2419 if (ElemTy != KnownTy) {
2421 propagateElemType(CI, ElemTy, VisitedSubst);
2427 for (
User *U :
Op->users()) {
2429 if (Inst && !isa<IntrinsicInst>(Inst))
2433 if (TodoTypeSz == 0)
2440 auto It = ToProcess.
find(&
I);
2441 if (It == ToProcess.
end())
2443 It->second.remove_if([
this](
Value *V) {
return !isTodoType(V); });
2444 if (It->second.size() == 0)
2446 deduceOperandElementType(&
I, &UncompleteRets, &It->second,
true);
2447 if (TodoTypeSz == 0)
2452 return SzTodo > TodoTypeSz;
2456void SPIRVEmitIntrinsics::parseFunDeclarations(
Module &M) {
2458 if (!
F.isDeclaration() ||
F.isIntrinsic())
2462 if (DemangledName.empty())
2465 auto [Grp, Opcode, ExtNo] =
2467 if (Opcode != SPIRV::OpGroupAsyncCopy)
2471 for (
unsigned OpIdx = 0; OpIdx <
F.arg_size(); ++OpIdx) {
2482 if (!TypeStrs.
size())
2485 for (
unsigned Idx : Idxs) {
2492 FDeclPtrTys[&
F].push_back(std::make_pair(
Idx, ElemTy));
2497bool SPIRVEmitIntrinsics::runOnModule(
Module &M) {
2498 bool Changed =
false;
2500 parseFunDeclarations(M);
2510 if (!
F.isDeclaration() && !
F.isIntrinsic()) {
2512 processParamTypes(&
F,
B);
2516 CanTodoType =
false;
2517 Changed |= postprocessTypes(M);
2520 Changed |= processFunctionPointers(M);
2526 return new SPIRVEmitIntrinsics(TM);
static unsigned getIntrinsicID(const SDNode *N)
Expand Atomic instructions
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static void replaceAllUsesWith(Value *Old, Value *New, SmallSet< BasicBlock *, 32 > &FreshBBs, bool IsHuge)
Replace all old uses with new ones, and push the updated BBs into FreshBBs.
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
This file defines the DenseSet and SmallDenseSet classes.
static bool runOnFunction(Function &F, bool PostInlining)
iv Induction Variable Users
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static bool isMemInstrToReplace(Instruction *I)
static bool isAggrConstForceInt32(const Value *V)
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 bool IsKernelArgInt8(Function *F, StoreInst *SI)
static void setInsertPointSkippingPhis(IRBuilder<> &B, Instruction *I)
static FunctionType * getFunctionPointerElemType(Function *F, SPIRVGlobalRegistry *GR)
static void createSaturatedConversionDecoration(Instruction *I, IRBuilder<> &B)
static Type * restoreMutatedType(SPIRVGlobalRegistry *GR, Instruction *I, Type *Ty)
static bool requireAssignType(Instruction *I)
void visit(MachineFunction &MF, MachineBasicBlock &Start, std::function< void(MachineBasicBlock *)> op)
static void insertSpirvDecorations(MachineFunction &MF, MachineIRBuilder MIB)
#define SPIRV_BACKEND_SERVICE_FUN_NAME
static SymbolRef::Type getType(const Symbol *Sym)
static int Lookup(ArrayRef< TableEntry > Table, unsigned Opcode)
an instruction to allocate memory on the stack
Represent the analysis usage information of a pass.
This class represents an incoming formal argument to a Function.
const Function * getParent() const
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
An instruction that atomically checks whether a specified value is in a memory location,...
static unsigned getPointerOperandIndex()
static unsigned getPointerOperandIndex()
LLVM Basic Block Representation.
iterator_range< const_phi_iterator > phis() const
Returns a range that iterates over the phis in the basic block.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
LLVMContext & getContext() const
Get the context in which this basic block lives.
This class represents a no-op cast from one type to another.
static 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...
bool isIndirectCall() const
Return true if the callsite is an indirect call.
Value * getCalledOperand() const
Value * getArgOperand(unsigned i) const
void setArgOperand(unsigned i, Value *v)
FunctionType * getFunctionType() const
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.
This is an important base class in LLVM.
static Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
This is the common base class for constrained floating point intrinsics.
std::optional< RoundingMode > getRoundingMode() const
This class represents an Operation in the Expression.
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)
Implements a dense probed hash-table based set.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
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 Type * getTypeAtIndex(Type *Ty, Value *Idx)
Return the type of the element at the given index of an indexable type.
static unsigned getPointerOperandIndex()
PointerType * getType() const
Global values are always pointers.
@ PrivateLinkage
Like Internal, but omit from symbol table.
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
bool hasInitializer() const
Definitions have initializers, declarations don't.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Indirect Branch Instruction.
void addDestination(BasicBlock *Dest)
Add a destination.
This instruction inserts a single (scalar) element into a VectorType value.
This instruction inserts a struct field of array element value into an aggregate value.
Base class for instruction visitors.
RetTy visitExtractElementInst(ExtractElementInst &I)
RetTy visitInsertValueInst(InsertValueInst &I)
RetTy visitUnreachableInst(UnreachableInst &I)
RetTy visitAtomicCmpXchgInst(AtomicCmpXchgInst &I)
RetTy visitBitCastInst(BitCastInst &I)
RetTy visitSwitchInst(SwitchInst &I)
RetTy visitExtractValueInst(ExtractValueInst &I)
RetTy visitStoreInst(StoreInst &I)
RetTy visitInsertElementInst(InsertElementInst &I)
RetTy visitAllocaInst(AllocaInst &I)
RetTy visitCallInst(CallInst &I)
RetTy visitGetElementPtrInst(GetElementPtrInst &I)
void visitInstruction(Instruction &I)
RetTy visitLoadInst(LoadInst &I)
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...
A wrapper class for inspecting calls to intrinsic functions.
This is an important class for using LLVM in a threaded context.
An instruction for reading from memory.
static unsigned getPointerOperandIndex()
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
static MDString * get(LLVMContext &Context, StringRef Str)
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Flags
Flags values. These may be or'd together.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
PassRegistry - This class manages the registration and intitialization of the pass subsystem as appli...
static PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
static PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
void addAssignPtrTypeInstr(Value *Val, CallInst *AssignPtrTyCI)
Type * findDeducedCompositeType(const Value *Val)
void addDeducedElementType(Value *Val, Type *Ty)
void addReturnType(const Function *ArgF, TypedPointerType *DerivedTy)
void updateIfExistDeducedElementType(Value *OldVal, Value *NewVal, bool DeleteOld)
Type * findMutated(const Value *Val)
void addDeducedCompositeType(Value *Val, Type *Ty)
void updateIfExistAssignPtrTypeInstr(Value *OldVal, Value *NewVal, bool DeleteOld)
Type * findDeducedElementType(const Value *Val)
CallInst * findAssignPtrTypeInstr(const Value *Val)
bool canUseExtension(SPIRV::Extension::Extension E) const
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
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
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()
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
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.
static StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Class to represent target extensions types, which are generally unintrospectable from target-independ...
static 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.
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 isPointerTy() const
True if this is an instance of PointerType.
StringRef getTargetExtName() const
static Type * getVoidTy(LLVMContext &C)
bool isTargetExtTy() const
Return true if this is a target extension type.
bool isAggregateType() const
Return true if the type is an aggregate type.
static IntegerType * getInt32Ty(LLVMContext &C)
bool isVoidTy() const
Return true if this is 'void'.
static bool isValidElementType(Type *ElemTy)
Return true if the specified type is valid as a element type.
static TypedPointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
static UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
This function has undefined behavior.
A Use represents the edge between a Value definition and its users.
Value * getOperand(unsigned i) const
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
void setName(const Twine &Name)
Change the name of the value.
iterator_range< user_iterator > users()
LLVMContext & getContext() const
All values hold a context through their type.
unsigned getNumUses() const
This method computes the number of uses of this Value.
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.
@ SPIR_KERNEL
Used for SPIR kernel functions.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
ID ArrayRef< Type * > Tys
std::string lookupBuiltinNameHelper(StringRef DemangledCall, FPDecorationId *DecorationId)
Parses the name part of the demangled builtin call.
Type * parseBuiltinCallArgumentType(StringRef TypeStr, LLVMContext &Ctx)
bool parseBuiltinTypeStr(SmallVector< StringRef, 10 > &BuiltinArgsTypeStrs, const StringRef DemangledCall, LLVMContext &Ctx)
std::tuple< int, unsigned, unsigned > mapBuiltinToOpcode(const StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set)
Helper function for finding a builtin function attributes by a demangled function name.
Type * parseBuiltinCallArgumentBaseType(const StringRef DemangledCall, unsigned ArgIdx, LLVMContext &Ctx)
Parses the provided ArgIdx argument base type in the DemangledCall skeleton.
NodeAddr< PhiNode * > Phi
NodeAddr< FuncNode * > Func
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.
bool getVacantFunctionName(Module &M, std::string &Name)
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &)
bool isTypedPointerWrapper(const TargetExtType *ExtTy)
ModulePass * createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM)
unsigned getPointerAddressSpace(const Type *T)
bool isNestedPointer(const Type *Ty)
std::string getOclOrSpirvBuiltinDemangledName(StringRef Name)
auto reverse(ContainerTy &&C)
Type * getTypedPointerWrapper(Type *ElemTy, unsigned AS)
bool isPointerTy(const Type *T)
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
SPIRV::Scope::Scope getMemScope(LLVMContext &Ctx, SyncScope::ID Id)
@ Ref
The access may reference the value stored in memory.
DWARFExpression::Operation Op
Type * getPointeeTypeByAttr(Argument *Arg)
bool hasPointeeTypeAttr(Argument *Arg)
bool isEquivalentTypes(Type *Ty1, Type *Ty2)
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID)
Type * getPointeeType(const Type *Ty)
void addStringImm(const StringRef &Str, MCInst &Inst)
bool isUntypedPointerTy(const Type *T)
SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord)