|  | //===-- SPIRVEmitIntrinsics.cpp - emit SPIRV intrinsics ---------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // The pass emits SPIRV intrinsics keeping essential high-level information for | 
|  | // the translation of LLVM IR to SPIR-V. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "SPIRV.h" | 
|  | #include "SPIRVBuiltins.h" | 
|  | #include "SPIRVMetadata.h" | 
|  | #include "SPIRVSubtarget.h" | 
|  | #include "SPIRVTargetMachine.h" | 
|  | #include "SPIRVUtils.h" | 
|  | #include "llvm/IR/IRBuilder.h" | 
|  | #include "llvm/IR/InstIterator.h" | 
|  | #include "llvm/IR/InstVisitor.h" | 
|  | #include "llvm/IR/IntrinsicsSPIRV.h" | 
|  | #include "llvm/IR/TypedPointerType.h" | 
|  |  | 
|  | #include <queue> | 
|  |  | 
|  | // This pass performs the following transformation on LLVM IR level required | 
|  | // for the following translation to SPIR-V: | 
|  | // - replaces direct usages of aggregate constants with target-specific | 
|  | //   intrinsics; | 
|  | // - replaces aggregates-related instructions (extract/insert, ld/st, etc) | 
|  | //   with a target-specific intrinsics; | 
|  | // - emits intrinsics for the global variable initializers since IRTranslator | 
|  | //   doesn't handle them and it's not very convenient to translate them | 
|  | //   ourselves; | 
|  | // - emits intrinsics to keep track of the string names assigned to the values; | 
|  | // - emits intrinsics to keep track of constants (this is necessary to have an | 
|  | //   LLVM IR constant after the IRTranslation is completed) for their further | 
|  | //   deduplication; | 
|  | // - emits intrinsics to keep track of original LLVM types of the values | 
|  | //   to be able to emit proper SPIR-V types eventually. | 
|  | // | 
|  | // TODO: consider removing spv.track.constant in favor of spv.assign.type. | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace llvm { | 
|  | void initializeSPIRVEmitIntrinsicsPass(PassRegistry &); | 
|  | } // namespace llvm | 
|  |  | 
|  | namespace { | 
|  | class SPIRVEmitIntrinsics | 
|  | : public ModulePass, | 
|  | public InstVisitor<SPIRVEmitIntrinsics, Instruction *> { | 
|  | SPIRVTargetMachine *TM = nullptr; | 
|  | SPIRVGlobalRegistry *GR = nullptr; | 
|  | Function *F = nullptr; | 
|  | bool TrackConstants = true; | 
|  | DenseMap<Instruction *, Constant *> AggrConsts; | 
|  | DenseMap<Instruction *, Type *> AggrConstTypes; | 
|  | DenseSet<Instruction *> AggrStores; | 
|  |  | 
|  | // a registry of created Intrinsic::spv_assign_ptr_type instructions | 
|  | DenseMap<Value *, CallInst *> AssignPtrTypeInstr; | 
|  |  | 
|  | // deduce element type of untyped pointers | 
|  | Type *deduceElementType(Value *I); | 
|  | Type *deduceElementTypeHelper(Value *I); | 
|  | Type *deduceElementTypeHelper(Value *I, std::unordered_set<Value *> &Visited); | 
|  | Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand, | 
|  | std::unordered_set<Value *> &Visited); | 
|  | Type *deduceElementTypeByUsersDeep(Value *Op, | 
|  | std::unordered_set<Value *> &Visited); | 
|  |  | 
|  | // deduce nested types of composites | 
|  | Type *deduceNestedTypeHelper(User *U); | 
|  | Type *deduceNestedTypeHelper(User *U, Type *Ty, | 
|  | std::unordered_set<Value *> &Visited); | 
|  |  | 
|  | // deduce Types of operands of the Instruction if possible | 
|  | void deduceOperandElementType(Instruction *I); | 
|  |  | 
|  | void preprocessCompositeConstants(IRBuilder<> &B); | 
|  | void preprocessUndefs(IRBuilder<> &B); | 
|  |  | 
|  | CallInst *buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef<Type *> Types, | 
|  | Value *Arg, Value *Arg2, ArrayRef<Constant *> Imms, | 
|  | IRBuilder<> &B) { | 
|  | ConstantAsMetadata *CM = ValueAsMetadata::getConstant(Arg); | 
|  | MDTuple *TyMD = MDNode::get(F->getContext(), CM); | 
|  | MetadataAsValue *VMD = MetadataAsValue::get(F->getContext(), TyMD); | 
|  | SmallVector<Value *, 4> Args; | 
|  | Args.push_back(Arg2); | 
|  | Args.push_back(VMD); | 
|  | for (auto *Imm : Imms) | 
|  | Args.push_back(Imm); | 
|  | return B.CreateIntrinsic(IntrID, {Types}, Args); | 
|  | } | 
|  |  | 
|  | void buildAssignPtr(IRBuilder<> &B, Type *ElemTy, Value *Arg); | 
|  |  | 
|  | void replaceMemInstrUses(Instruction *Old, Instruction *New, IRBuilder<> &B); | 
|  | void processInstrAfterVisit(Instruction *I, IRBuilder<> &B); | 
|  | void insertAssignPtrTypeIntrs(Instruction *I, IRBuilder<> &B); | 
|  | void insertAssignTypeIntrs(Instruction *I, IRBuilder<> &B); | 
|  | void insertAssignTypeInstrForTargetExtTypes(TargetExtType *AssignedType, | 
|  | Value *V, IRBuilder<> &B); | 
|  | void replacePointerOperandWithPtrCast(Instruction *I, Value *Pointer, | 
|  | Type *ExpectedElementType, | 
|  | unsigned OperandToReplace, | 
|  | IRBuilder<> &B); | 
|  | void insertPtrCastOrAssignTypeInstr(Instruction *I, IRBuilder<> &B); | 
|  | void processGlobalValue(GlobalVariable &GV, IRBuilder<> &B); | 
|  | void processParamTypes(Function *F, IRBuilder<> &B); | 
|  | void processParamTypesByFunHeader(Function *F, IRBuilder<> &B); | 
|  | Type *deduceFunParamElementType(Function *F, unsigned OpIdx); | 
|  | Type *deduceFunParamElementType(Function *F, unsigned OpIdx, | 
|  | std::unordered_set<Function *> &FVisited); | 
|  |  | 
|  | public: | 
|  | static char ID; | 
|  | SPIRVEmitIntrinsics() : ModulePass(ID) { | 
|  | initializeSPIRVEmitIntrinsicsPass(*PassRegistry::getPassRegistry()); | 
|  | } | 
|  | SPIRVEmitIntrinsics(SPIRVTargetMachine *_TM) : ModulePass(ID), TM(_TM) { | 
|  | initializeSPIRVEmitIntrinsicsPass(*PassRegistry::getPassRegistry()); | 
|  | } | 
|  | Instruction *visitInstruction(Instruction &I) { return &I; } | 
|  | Instruction *visitSwitchInst(SwitchInst &I); | 
|  | Instruction *visitGetElementPtrInst(GetElementPtrInst &I); | 
|  | Instruction *visitBitCastInst(BitCastInst &I); | 
|  | Instruction *visitInsertElementInst(InsertElementInst &I); | 
|  | Instruction *visitExtractElementInst(ExtractElementInst &I); | 
|  | Instruction *visitInsertValueInst(InsertValueInst &I); | 
|  | Instruction *visitExtractValueInst(ExtractValueInst &I); | 
|  | Instruction *visitLoadInst(LoadInst &I); | 
|  | Instruction *visitStoreInst(StoreInst &I); | 
|  | Instruction *visitAllocaInst(AllocaInst &I); | 
|  | Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I); | 
|  | Instruction *visitUnreachableInst(UnreachableInst &I); | 
|  |  | 
|  | StringRef getPassName() const override { return "SPIRV emit intrinsics"; } | 
|  |  | 
|  | bool runOnModule(Module &M) override; | 
|  | bool runOnFunction(Function &F); | 
|  |  | 
|  | void getAnalysisUsage(AnalysisUsage &AU) const override { | 
|  | ModulePass::getAnalysisUsage(AU); | 
|  | } | 
|  | }; | 
|  | } // namespace | 
|  |  | 
|  | char SPIRVEmitIntrinsics::ID = 0; | 
|  |  | 
|  | INITIALIZE_PASS(SPIRVEmitIntrinsics, "emit-intrinsics", "SPIRV emit intrinsics", | 
|  | false, false) | 
|  |  | 
|  | static inline bool isAssignTypeInstr(const Instruction *I) { | 
|  | return isa<IntrinsicInst>(I) && | 
|  | cast<IntrinsicInst>(I)->getIntrinsicID() == Intrinsic::spv_assign_type; | 
|  | } | 
|  |  | 
|  | static bool isMemInstrToReplace(Instruction *I) { | 
|  | return isa<StoreInst>(I) || isa<LoadInst>(I) || isa<InsertValueInst>(I) || | 
|  | isa<ExtractValueInst>(I) || isa<AtomicCmpXchgInst>(I); | 
|  | } | 
|  |  | 
|  | static bool isAggrToReplace(const Value *V) { | 
|  | return isa<ConstantAggregate>(V) || isa<ConstantDataArray>(V) || | 
|  | (isa<ConstantAggregateZero>(V) && !V->getType()->isVectorTy()); | 
|  | } | 
|  |  | 
|  | static void setInsertPointSkippingPhis(IRBuilder<> &B, Instruction *I) { | 
|  | if (isa<PHINode>(I)) | 
|  | B.SetInsertPoint(I->getParent(), I->getParent()->getFirstInsertionPt()); | 
|  | else | 
|  | B.SetInsertPoint(I); | 
|  | } | 
|  |  | 
|  | static bool requireAssignType(Instruction *I) { | 
|  | IntrinsicInst *Intr = dyn_cast<IntrinsicInst>(I); | 
|  | if (Intr) { | 
|  | switch (Intr->getIntrinsicID()) { | 
|  | case Intrinsic::invariant_start: | 
|  | case Intrinsic::invariant_end: | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static inline void reportFatalOnTokenType(const Instruction *I) { | 
|  | if (I->getType()->isTokenTy()) | 
|  | report_fatal_error("A token is encountered but SPIR-V without extensions " | 
|  | "does not support token type", | 
|  | false); | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::buildAssignPtr(IRBuilder<> &B, Type *ElemTy, | 
|  | Value *Arg) { | 
|  | CallInst *AssignPtrTyCI = | 
|  | buildIntrWithMD(Intrinsic::spv_assign_ptr_type, {Arg->getType()}, | 
|  | Constant::getNullValue(ElemTy), Arg, | 
|  | {B.getInt32(getPointerAddressSpace(Arg->getType()))}, B); | 
|  | GR->addDeducedElementType(AssignPtrTyCI, ElemTy); | 
|  | GR->addDeducedElementType(Arg, ElemTy); | 
|  | AssignPtrTypeInstr[Arg] = AssignPtrTyCI; | 
|  | } | 
|  |  | 
|  | // Set element pointer type to the given value of ValueTy and tries to | 
|  | // specify this type further (recursively) by Operand value, if needed. | 
|  | Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep( | 
|  | Type *ValueTy, Value *Operand, std::unordered_set<Value *> &Visited) { | 
|  | Type *Ty = ValueTy; | 
|  | if (Operand) { | 
|  | if (auto *PtrTy = dyn_cast<PointerType>(Ty)) { | 
|  | if (Type *NestedTy = deduceElementTypeHelper(Operand, Visited)) | 
|  | Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); | 
|  | } else { | 
|  | Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited); | 
|  | } | 
|  | } | 
|  | return Ty; | 
|  | } | 
|  |  | 
|  | // Traverse User instructions to deduce an element pointer type of the operand. | 
|  | Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep( | 
|  | Value *Op, std::unordered_set<Value *> &Visited) { | 
|  | if (!Op || !isPointerTy(Op->getType())) | 
|  | return nullptr; | 
|  |  | 
|  | if (auto PType = dyn_cast<TypedPointerType>(Op->getType())) | 
|  | return PType->getElementType(); | 
|  |  | 
|  | // maybe we already know operand's element type | 
|  | if (Type *KnownTy = GR->findDeducedElementType(Op)) | 
|  | return KnownTy; | 
|  |  | 
|  | for (User *OpU : Op->users()) { | 
|  | if (Instruction *Inst = dyn_cast<Instruction>(OpU)) { | 
|  | if (Type *Ty = deduceElementTypeHelper(Inst, Visited)) | 
|  | return Ty; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Implements what we know in advance about intrinsics and builtin calls | 
|  | // TODO: consider feasibility of this particular case to be generalized by | 
|  | // encoding knowledge about intrinsics and builtin calls by corresponding | 
|  | // specification rules | 
|  | static Type *getPointeeTypeByCallInst(StringRef DemangledName, | 
|  | Function *CalledF, unsigned OpIdx) { | 
|  | if ((DemangledName.starts_with("__spirv_ocl_printf(") || | 
|  | DemangledName.starts_with("printf(")) && | 
|  | OpIdx == 0) | 
|  | return IntegerType::getInt8Ty(CalledF->getContext()); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Deduce and return a successfully deduced Type of the Instruction, | 
|  | // or nullptr otherwise. | 
|  | Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(Value *I) { | 
|  | std::unordered_set<Value *> Visited; | 
|  | return deduceElementTypeHelper(I, Visited); | 
|  | } | 
|  |  | 
|  | Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( | 
|  | Value *I, std::unordered_set<Value *> &Visited) { | 
|  | // allow to pass nullptr as an argument | 
|  | if (!I) | 
|  | return nullptr; | 
|  |  | 
|  | // maybe already known | 
|  | if (Type *KnownTy = GR->findDeducedElementType(I)) | 
|  | return KnownTy; | 
|  |  | 
|  | // maybe a cycle | 
|  | if (Visited.find(I) != Visited.end()) | 
|  | return nullptr; | 
|  | Visited.insert(I); | 
|  |  | 
|  | // fallback value in case when we fail to deduce a type | 
|  | Type *Ty = nullptr; | 
|  | // look for known basic patterns of type inference | 
|  | if (auto *Ref = dyn_cast<AllocaInst>(I)) { | 
|  | Ty = Ref->getAllocatedType(); | 
|  | } else if (auto *Ref = dyn_cast<GetElementPtrInst>(I)) { | 
|  | Ty = Ref->getResultElementType(); | 
|  | } else if (auto *Ref = dyn_cast<GlobalValue>(I)) { | 
|  | Ty = deduceElementTypeByValueDeep( | 
|  | Ref->getValueType(), | 
|  | Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr, Visited); | 
|  | } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) { | 
|  | Ty = deduceElementTypeHelper(Ref->getPointerOperand(), Visited); | 
|  | } else if (auto *Ref = dyn_cast<BitCastInst>(I)) { | 
|  | if (Type *Src = Ref->getSrcTy(), *Dest = Ref->getDestTy(); | 
|  | isPointerTy(Src) && isPointerTy(Dest)) | 
|  | Ty = deduceElementTypeHelper(Ref->getOperand(0), Visited); | 
|  | } else if (auto *Ref = dyn_cast<AtomicCmpXchgInst>(I)) { | 
|  | Value *Op = Ref->getNewValOperand(); | 
|  | Ty = deduceElementTypeByValueDeep(Op->getType(), Op, Visited); | 
|  | } else if (auto *Ref = dyn_cast<AtomicRMWInst>(I)) { | 
|  | Value *Op = Ref->getValOperand(); | 
|  | Ty = deduceElementTypeByValueDeep(Op->getType(), Op, Visited); | 
|  | } else if (auto *Ref = dyn_cast<PHINode>(I)) { | 
|  | for (unsigned i = 0; i < Ref->getNumIncomingValues(); i++) { | 
|  | Ty = deduceElementTypeByUsersDeep(Ref->getIncomingValue(i), Visited); | 
|  | if (Ty) | 
|  | break; | 
|  | } | 
|  | } else if (auto *Ref = dyn_cast<SelectInst>(I)) { | 
|  | for (Value *Op : {Ref->getTrueValue(), Ref->getFalseValue()}) { | 
|  | Ty = deduceElementTypeByUsersDeep(Op, Visited); | 
|  | if (Ty) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // remember the found relationship | 
|  | if (Ty) { | 
|  | // specify nested types if needed, otherwise return unchanged | 
|  | GR->addDeducedElementType(I, Ty); | 
|  | } | 
|  |  | 
|  | return Ty; | 
|  | } | 
|  |  | 
|  | // Re-create a type of the value if it has untyped pointer fields, also nested. | 
|  | // Return the original value type if no corrections of untyped pointer | 
|  | // information is found or needed. | 
|  | Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U) { | 
|  | std::unordered_set<Value *> Visited; | 
|  | return deduceNestedTypeHelper(U, U->getType(), Visited); | 
|  | } | 
|  |  | 
|  | Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper( | 
|  | User *U, Type *OrigTy, std::unordered_set<Value *> &Visited) { | 
|  | if (!U) | 
|  | return OrigTy; | 
|  |  | 
|  | // maybe already known | 
|  | if (Type *KnownTy = GR->findDeducedCompositeType(U)) | 
|  | return KnownTy; | 
|  |  | 
|  | // maybe a cycle | 
|  | if (Visited.find(U) != Visited.end()) | 
|  | return OrigTy; | 
|  | Visited.insert(U); | 
|  |  | 
|  | if (dyn_cast<StructType>(OrigTy)) { | 
|  | SmallVector<Type *> Tys; | 
|  | bool Change = false; | 
|  | for (unsigned i = 0; i < U->getNumOperands(); ++i) { | 
|  | Value *Op = U->getOperand(i); | 
|  | Type *OpTy = Op->getType(); | 
|  | Type *Ty = OpTy; | 
|  | if (Op) { | 
|  | if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) { | 
|  | if (Type *NestedTy = deduceElementTypeHelper(Op, Visited)) | 
|  | Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); | 
|  | } else { | 
|  | Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited); | 
|  | } | 
|  | } | 
|  | Tys.push_back(Ty); | 
|  | Change |= Ty != OpTy; | 
|  | } | 
|  | if (Change) { | 
|  | Type *NewTy = StructType::create(Tys); | 
|  | GR->addDeducedCompositeType(U, NewTy); | 
|  | return NewTy; | 
|  | } | 
|  | } else if (auto *ArrTy = dyn_cast<ArrayType>(OrigTy)) { | 
|  | if (Value *Op = U->getNumOperands() > 0 ? U->getOperand(0) : nullptr) { | 
|  | Type *OpTy = ArrTy->getElementType(); | 
|  | Type *Ty = OpTy; | 
|  | if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) { | 
|  | if (Type *NestedTy = deduceElementTypeHelper(Op, Visited)) | 
|  | Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); | 
|  | } else { | 
|  | Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited); | 
|  | } | 
|  | if (Ty != OpTy) { | 
|  | Type *NewTy = ArrayType::get(Ty, ArrTy->getNumElements()); | 
|  | GR->addDeducedCompositeType(U, NewTy); | 
|  | return NewTy; | 
|  | } | 
|  | } | 
|  | } else if (auto *VecTy = dyn_cast<VectorType>(OrigTy)) { | 
|  | if (Value *Op = U->getNumOperands() > 0 ? U->getOperand(0) : nullptr) { | 
|  | Type *OpTy = VecTy->getElementType(); | 
|  | Type *Ty = OpTy; | 
|  | if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) { | 
|  | if (Type *NestedTy = deduceElementTypeHelper(Op, Visited)) | 
|  | Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace()); | 
|  | } else { | 
|  | Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited); | 
|  | } | 
|  | if (Ty != OpTy) { | 
|  | Type *NewTy = VectorType::get(Ty, VecTy->getElementCount()); | 
|  | GR->addDeducedCompositeType(U, NewTy); | 
|  | return NewTy; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return OrigTy; | 
|  | } | 
|  |  | 
|  | Type *SPIRVEmitIntrinsics::deduceElementType(Value *I) { | 
|  | if (Type *Ty = deduceElementTypeHelper(I)) | 
|  | return Ty; | 
|  | return IntegerType::getInt8Ty(I->getContext()); | 
|  | } | 
|  |  | 
|  | // If the Instruction has Pointer operands with unresolved types, this function | 
|  | // tries to deduce them. If the Instruction has Pointer operands with known | 
|  | // types which differ from expected, this function tries to insert a bitcast to | 
|  | // resolve the issue. | 
|  | void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) { | 
|  | SmallVector<std::pair<Value *, unsigned>> Ops; | 
|  | Type *KnownElemTy = nullptr; | 
|  | // look for known basic patterns of type inference | 
|  | if (auto *Ref = dyn_cast<PHINode>(I)) { | 
|  | if (!isPointerTy(I->getType()) || | 
|  | !(KnownElemTy = GR->findDeducedElementType(I))) | 
|  | return; | 
|  | for (unsigned i = 0; i < Ref->getNumIncomingValues(); i++) { | 
|  | Value *Op = Ref->getIncomingValue(i); | 
|  | if (isPointerTy(Op->getType())) | 
|  | Ops.push_back(std::make_pair(Op, i)); | 
|  | } | 
|  | } else if (auto *Ref = dyn_cast<SelectInst>(I)) { | 
|  | if (!isPointerTy(I->getType()) || | 
|  | !(KnownElemTy = GR->findDeducedElementType(I))) | 
|  | return; | 
|  | for (unsigned i = 0; i < Ref->getNumOperands(); i++) { | 
|  | Value *Op = Ref->getOperand(i); | 
|  | if (isPointerTy(Op->getType())) | 
|  | Ops.push_back(std::make_pair(Op, i)); | 
|  | } | 
|  | } else if (auto *Ref = dyn_cast<ReturnInst>(I)) { | 
|  | Type *RetTy = F->getReturnType(); | 
|  | if (!isPointerTy(RetTy)) | 
|  | return; | 
|  | Value *Op = Ref->getReturnValue(); | 
|  | if (!Op) | 
|  | return; | 
|  | if (!(KnownElemTy = GR->findDeducedElementType(F))) { | 
|  | if (Type *OpElemTy = GR->findDeducedElementType(Op)) { | 
|  | GR->addDeducedElementType(F, OpElemTy); | 
|  | TypedPointerType *DerivedTy = | 
|  | TypedPointerType::get(OpElemTy, getPointerAddressSpace(RetTy)); | 
|  | GR->addReturnType(F, DerivedTy); | 
|  | } | 
|  | return; | 
|  | } | 
|  | Ops.push_back(std::make_pair(Op, 0)); | 
|  | } else if (auto *Ref = dyn_cast<ICmpInst>(I)) { | 
|  | if (!isPointerTy(Ref->getOperand(0)->getType())) | 
|  | return; | 
|  | Value *Op0 = Ref->getOperand(0); | 
|  | Value *Op1 = Ref->getOperand(1); | 
|  | Type *ElemTy0 = GR->findDeducedElementType(Op0); | 
|  | Type *ElemTy1 = GR->findDeducedElementType(Op1); | 
|  | if (ElemTy0) { | 
|  | KnownElemTy = ElemTy0; | 
|  | Ops.push_back(std::make_pair(Op1, 1)); | 
|  | } else if (ElemTy1) { | 
|  | KnownElemTy = ElemTy1; | 
|  | Ops.push_back(std::make_pair(Op0, 0)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // There is no enough info to deduce types or all is valid. | 
|  | if (!KnownElemTy || Ops.size() == 0) | 
|  | return; | 
|  |  | 
|  | LLVMContext &Ctx = F->getContext(); | 
|  | IRBuilder<> B(Ctx); | 
|  | for (auto &OpIt : Ops) { | 
|  | Value *Op = OpIt.first; | 
|  | if (Op->use_empty()) | 
|  | continue; | 
|  | Type *Ty = GR->findDeducedElementType(Op); | 
|  | if (Ty == KnownElemTy) | 
|  | continue; | 
|  | if (Instruction *User = dyn_cast<Instruction>(Op->use_begin()->get())) | 
|  | setInsertPointSkippingPhis(B, User->getNextNode()); | 
|  | else | 
|  | B.SetInsertPoint(I); | 
|  | Value *OpTyVal = Constant::getNullValue(KnownElemTy); | 
|  | Type *OpTy = Op->getType(); | 
|  | if (!Ty) { | 
|  | GR->addDeducedElementType(Op, KnownElemTy); | 
|  | // check if there is existing Intrinsic::spv_assign_ptr_type instruction | 
|  | auto It = AssignPtrTypeInstr.find(Op); | 
|  | if (It == AssignPtrTypeInstr.end()) { | 
|  | CallInst *CI = | 
|  | buildIntrWithMD(Intrinsic::spv_assign_ptr_type, {OpTy}, OpTyVal, Op, | 
|  | {B.getInt32(getPointerAddressSpace(OpTy))}, B); | 
|  | AssignPtrTypeInstr[Op] = CI; | 
|  | } else { | 
|  | It->second->setArgOperand( | 
|  | 1, | 
|  | MetadataAsValue::get( | 
|  | Ctx, MDNode::get(Ctx, ValueAsMetadata::getConstant(OpTyVal)))); | 
|  | } | 
|  | } else { | 
|  | SmallVector<Type *, 2> Types = {OpTy, OpTy}; | 
|  | MetadataAsValue *VMD = MetadataAsValue::get( | 
|  | Ctx, MDNode::get(Ctx, ValueAsMetadata::getConstant(OpTyVal))); | 
|  | SmallVector<Value *, 2> Args = {Op, VMD, | 
|  | B.getInt32(getPointerAddressSpace(OpTy))}; | 
|  | CallInst *PtrCastI = | 
|  | B.CreateIntrinsic(Intrinsic::spv_ptrcast, {Types}, Args); | 
|  | I->setOperand(OpIt.second, PtrCastI); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old, | 
|  | Instruction *New, | 
|  | IRBuilder<> &B) { | 
|  | while (!Old->user_empty()) { | 
|  | auto *U = Old->user_back(); | 
|  | if (isAssignTypeInstr(U)) { | 
|  | B.SetInsertPoint(U); | 
|  | SmallVector<Value *, 2> Args = {New, U->getOperand(1)}; | 
|  | B.CreateIntrinsic(Intrinsic::spv_assign_type, {New->getType()}, Args); | 
|  | U->eraseFromParent(); | 
|  | } else if (isMemInstrToReplace(U) || isa<ReturnInst>(U) || | 
|  | isa<CallInst>(U)) { | 
|  | U->replaceUsesOfWith(Old, New); | 
|  | } else { | 
|  | llvm_unreachable("illegal aggregate intrinsic user"); | 
|  | } | 
|  | } | 
|  | Old->eraseFromParent(); | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::preprocessUndefs(IRBuilder<> &B) { | 
|  | std::queue<Instruction *> Worklist; | 
|  | for (auto &I : instructions(F)) | 
|  | Worklist.push(&I); | 
|  |  | 
|  | while (!Worklist.empty()) { | 
|  | Instruction *I = Worklist.front(); | 
|  | Worklist.pop(); | 
|  |  | 
|  | for (auto &Op : I->operands()) { | 
|  | auto *AggrUndef = dyn_cast<UndefValue>(Op); | 
|  | if (!AggrUndef || !Op->getType()->isAggregateType()) | 
|  | continue; | 
|  |  | 
|  | B.SetInsertPoint(I); | 
|  | auto *IntrUndef = B.CreateIntrinsic(Intrinsic::spv_undef, {}, {}); | 
|  | Worklist.push(IntrUndef); | 
|  | I->replaceUsesOfWith(Op, IntrUndef); | 
|  | AggrConsts[IntrUndef] = AggrUndef; | 
|  | AggrConstTypes[IntrUndef] = AggrUndef->getType(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) { | 
|  | std::queue<Instruction *> Worklist; | 
|  | for (auto &I : instructions(F)) | 
|  | Worklist.push(&I); | 
|  |  | 
|  | while (!Worklist.empty()) { | 
|  | auto *I = Worklist.front(); | 
|  | assert(I); | 
|  | bool KeepInst = false; | 
|  | for (const auto &Op : I->operands()) { | 
|  | auto BuildCompositeIntrinsic = | 
|  | [](Constant *AggrC, ArrayRef<Value *> Args, Value *Op, Instruction *I, | 
|  | IRBuilder<> &B, std::queue<Instruction *> &Worklist, | 
|  | bool &KeepInst, SPIRVEmitIntrinsics &SEI) { | 
|  | B.SetInsertPoint(I); | 
|  | auto *CCI = | 
|  | B.CreateIntrinsic(Intrinsic::spv_const_composite, {}, {Args}); | 
|  | Worklist.push(CCI); | 
|  | I->replaceUsesOfWith(Op, CCI); | 
|  | KeepInst = true; | 
|  | SEI.AggrConsts[CCI] = AggrC; | 
|  | SEI.AggrConstTypes[CCI] = SEI.deduceNestedTypeHelper(AggrC); | 
|  | }; | 
|  |  | 
|  | if (auto *AggrC = dyn_cast<ConstantAggregate>(Op)) { | 
|  | SmallVector<Value *> Args(AggrC->op_begin(), AggrC->op_end()); | 
|  | BuildCompositeIntrinsic(AggrC, Args, Op, I, B, Worklist, KeepInst, | 
|  | *this); | 
|  | } else if (auto *AggrC = dyn_cast<ConstantDataArray>(Op)) { | 
|  | SmallVector<Value *> Args; | 
|  | for (unsigned i = 0; i < AggrC->getNumElements(); ++i) | 
|  | Args.push_back(AggrC->getElementAsConstant(i)); | 
|  | BuildCompositeIntrinsic(AggrC, Args, Op, I, B, Worklist, KeepInst, | 
|  | *this); | 
|  | } else if (isa<ConstantAggregateZero>(Op) && | 
|  | !Op->getType()->isVectorTy()) { | 
|  | auto *AggrC = cast<ConstantAggregateZero>(Op); | 
|  | SmallVector<Value *> Args(AggrC->op_begin(), AggrC->op_end()); | 
|  | BuildCompositeIntrinsic(AggrC, Args, Op, I, B, Worklist, KeepInst, | 
|  | *this); | 
|  | } | 
|  | } | 
|  | if (!KeepInst) | 
|  | Worklist.pop(); | 
|  | } | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) { | 
|  | BasicBlock *ParentBB = I.getParent(); | 
|  | IRBuilder<> B(ParentBB); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Value *, 4> Args; | 
|  | SmallVector<BasicBlock *> BBCases; | 
|  | for (auto &Op : I.operands()) { | 
|  | if (Op.get()->getType()->isSized()) { | 
|  | Args.push_back(Op); | 
|  | } else if (BasicBlock *BB = dyn_cast<BasicBlock>(Op.get())) { | 
|  | BBCases.push_back(BB); | 
|  | Args.push_back(BlockAddress::get(BB->getParent(), BB)); | 
|  | } else { | 
|  | report_fatal_error("Unexpected switch operand"); | 
|  | } | 
|  | } | 
|  | CallInst *NewI = B.CreateIntrinsic(Intrinsic::spv_switch, | 
|  | {I.getOperand(0)->getType()}, {Args}); | 
|  | // remove switch to avoid its unneeded and undesirable unwrap into branches | 
|  | // and conditions | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | // insert artificial and temporary instruction to preserve valid CFG, | 
|  | // it will be removed after IR translation pass | 
|  | B.SetInsertPoint(ParentBB); | 
|  | IndirectBrInst *BrI = B.CreateIndirectBr( | 
|  | Constant::getNullValue(PointerType::getUnqual(ParentBB->getContext())), | 
|  | BBCases.size()); | 
|  | for (BasicBlock *BBCase : BBCases) | 
|  | BrI->addDestination(BBCase); | 
|  | return BrI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &I) { | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Type *, 2> Types = {I.getType(), I.getOperand(0)->getType()}; | 
|  | SmallVector<Value *, 4> Args; | 
|  | Args.push_back(B.getInt1(I.isInBounds())); | 
|  | for (auto &Op : I.operands()) | 
|  | Args.push_back(Op); | 
|  | auto *NewI = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args}); | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &I) { | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | Value *Source = I.getOperand(0); | 
|  |  | 
|  | // SPIR-V, contrary to LLVM 17+ IR, supports bitcasts between pointers of | 
|  | // varying element types. In case of IR coming from older versions of LLVM | 
|  | // such bitcasts do not provide sufficient information, should be just skipped | 
|  | // here, and handled in insertPtrCastOrAssignTypeInstr. | 
|  | if (isPointerTy(I.getType())) { | 
|  | I.replaceAllUsesWith(Source); | 
|  | I.eraseFromParent(); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | SmallVector<Type *, 2> Types = {I.getType(), Source->getType()}; | 
|  | SmallVector<Value *> Args(I.op_begin(), I.op_end()); | 
|  | auto *NewI = B.CreateIntrinsic(Intrinsic::spv_bitcast, {Types}, {Args}); | 
|  | std::string InstName = I.hasName() ? I.getName().str() : ""; | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | NewI->setName(InstName); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::insertAssignTypeInstrForTargetExtTypes( | 
|  | TargetExtType *AssignedType, Value *V, IRBuilder<> &B) { | 
|  | // Do not emit spv_assign_type if the V is of the AssignedType already. | 
|  | if (V->getType() == AssignedType) | 
|  | return; | 
|  |  | 
|  | // Do not emit spv_assign_type if there is one already targetting V. If the | 
|  | // found spv_assign_type assigns a type different than AssignedType, report an | 
|  | // error. Builtin types cannot be redeclared or casted. | 
|  | for (auto User : V->users()) { | 
|  | auto *II = dyn_cast<IntrinsicInst>(User); | 
|  | if (!II || II->getIntrinsicID() != Intrinsic::spv_assign_type) | 
|  | continue; | 
|  |  | 
|  | MetadataAsValue *VMD = cast<MetadataAsValue>(II->getOperand(1)); | 
|  | Type *BuiltinType = | 
|  | dyn_cast<ConstantAsMetadata>(VMD->getMetadata())->getType(); | 
|  | if (BuiltinType != AssignedType) | 
|  | report_fatal_error("Type mismatch " + BuiltinType->getTargetExtName() + | 
|  | "/" + AssignedType->getTargetExtName() + | 
|  | " for value " + V->getName(), | 
|  | false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Constant *Const = UndefValue::get(AssignedType); | 
|  | buildIntrWithMD(Intrinsic::spv_assign_type, {V->getType()}, Const, V, {}, B); | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::replacePointerOperandWithPtrCast( | 
|  | Instruction *I, Value *Pointer, Type *ExpectedElementType, | 
|  | unsigned OperandToReplace, IRBuilder<> &B) { | 
|  | // If Pointer is the result of nop BitCastInst (ptr -> ptr), use the source | 
|  | // pointer instead. The BitCastInst should be later removed when visited. | 
|  | while (BitCastInst *BC = dyn_cast<BitCastInst>(Pointer)) | 
|  | Pointer = BC->getOperand(0); | 
|  |  | 
|  | // Do not emit spv_ptrcast if Pointer's element type is ExpectedElementType | 
|  | Type *PointerElemTy = deduceElementTypeHelper(Pointer); | 
|  | if (PointerElemTy == ExpectedElementType) | 
|  | return; | 
|  |  | 
|  | setInsertPointSkippingPhis(B, I); | 
|  | Constant *ExpectedElementTypeConst = | 
|  | Constant::getNullValue(ExpectedElementType); | 
|  | ConstantAsMetadata *CM = | 
|  | ValueAsMetadata::getConstant(ExpectedElementTypeConst); | 
|  | MDTuple *TyMD = MDNode::get(F->getContext(), CM); | 
|  | MetadataAsValue *VMD = MetadataAsValue::get(F->getContext(), TyMD); | 
|  | unsigned AddressSpace = getPointerAddressSpace(Pointer->getType()); | 
|  | bool FirstPtrCastOrAssignPtrType = true; | 
|  |  | 
|  | // Do not emit new spv_ptrcast if equivalent one already exists or when | 
|  | // spv_assign_ptr_type already targets this pointer with the same element | 
|  | // type. | 
|  | for (auto User : Pointer->users()) { | 
|  | auto *II = dyn_cast<IntrinsicInst>(User); | 
|  | if (!II || | 
|  | (II->getIntrinsicID() != Intrinsic::spv_assign_ptr_type && | 
|  | II->getIntrinsicID() != Intrinsic::spv_ptrcast) || | 
|  | II->getOperand(0) != Pointer) | 
|  | continue; | 
|  |  | 
|  | // There is some spv_ptrcast/spv_assign_ptr_type already targeting this | 
|  | // pointer. | 
|  | FirstPtrCastOrAssignPtrType = false; | 
|  | if (II->getOperand(1) != VMD || | 
|  | dyn_cast<ConstantInt>(II->getOperand(2))->getSExtValue() != | 
|  | AddressSpace) | 
|  | continue; | 
|  |  | 
|  | // The spv_ptrcast/spv_assign_ptr_type targeting this pointer is of the same | 
|  | // element type and address space. | 
|  | if (II->getIntrinsicID() != Intrinsic::spv_ptrcast) | 
|  | return; | 
|  |  | 
|  | // This must be a spv_ptrcast, do not emit new if this one has the same BB | 
|  | // as I. Otherwise, search for other spv_ptrcast/spv_assign_ptr_type. | 
|  | if (II->getParent() != I->getParent()) | 
|  | continue; | 
|  |  | 
|  | I->setOperand(OperandToReplace, II); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // // Do not emit spv_ptrcast if it would cast to the default pointer element | 
|  | // // type (i8) of the same address space. | 
|  | // if (ExpectedElementType->isIntegerTy(8)) | 
|  | //   return; | 
|  |  | 
|  | // If this would be the first spv_ptrcast, do not emit spv_ptrcast and emit | 
|  | // spv_assign_ptr_type instead. | 
|  | if (FirstPtrCastOrAssignPtrType && | 
|  | (isa<Instruction>(Pointer) || isa<Argument>(Pointer))) { | 
|  | CallInst *CI = buildIntrWithMD( | 
|  | Intrinsic::spv_assign_ptr_type, {Pointer->getType()}, | 
|  | ExpectedElementTypeConst, Pointer, {B.getInt32(AddressSpace)}, B); | 
|  | GR->addDeducedElementType(CI, ExpectedElementType); | 
|  | GR->addDeducedElementType(Pointer, ExpectedElementType); | 
|  | AssignPtrTypeInstr[Pointer] = CI; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Emit spv_ptrcast | 
|  | SmallVector<Type *, 2> Types = {Pointer->getType(), Pointer->getType()}; | 
|  | SmallVector<Value *, 2> Args = {Pointer, VMD, B.getInt32(AddressSpace)}; | 
|  | auto *PtrCastI = B.CreateIntrinsic(Intrinsic::spv_ptrcast, {Types}, Args); | 
|  | I->setOperand(OperandToReplace, PtrCastI); | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I, | 
|  | IRBuilder<> &B) { | 
|  | // Handle basic instructions: | 
|  | StoreInst *SI = dyn_cast<StoreInst>(I); | 
|  | if (SI && F->getCallingConv() == CallingConv::SPIR_KERNEL && | 
|  | isPointerTy(SI->getValueOperand()->getType()) && | 
|  | isa<Argument>(SI->getValueOperand())) { | 
|  | return replacePointerOperandWithPtrCast( | 
|  | I, SI->getValueOperand(), IntegerType::getInt8Ty(F->getContext()), 0, | 
|  | B); | 
|  | } else if (SI) { | 
|  | return replacePointerOperandWithPtrCast( | 
|  | I, SI->getPointerOperand(), SI->getValueOperand()->getType(), 1, B); | 
|  | } else if (LoadInst *LI = dyn_cast<LoadInst>(I)) { | 
|  | return replacePointerOperandWithPtrCast(I, LI->getPointerOperand(), | 
|  | LI->getType(), 0, B); | 
|  | } else if (GetElementPtrInst *GEPI = dyn_cast<GetElementPtrInst>(I)) { | 
|  | return replacePointerOperandWithPtrCast(I, GEPI->getPointerOperand(), | 
|  | GEPI->getSourceElementType(), 0, B); | 
|  | } | 
|  |  | 
|  | // Handle calls to builtins (non-intrinsics): | 
|  | CallInst *CI = dyn_cast<CallInst>(I); | 
|  | if (!CI || CI->isIndirectCall() || CI->isInlineAsm() || | 
|  | !CI->getCalledFunction() || CI->getCalledFunction()->isIntrinsic()) | 
|  | return; | 
|  |  | 
|  | // collect information about formal parameter types | 
|  | std::string DemangledName = | 
|  | getOclOrSpirvBuiltinDemangledName(CI->getCalledFunction()->getName()); | 
|  | Function *CalledF = CI->getCalledFunction(); | 
|  | SmallVector<Type *, 4> CalledArgTys; | 
|  | bool HaveTypes = false; | 
|  | for (unsigned OpIdx = 0; OpIdx < CalledF->arg_size(); ++OpIdx) { | 
|  | Argument *CalledArg = CalledF->getArg(OpIdx); | 
|  | Type *ArgType = CalledArg->getType(); | 
|  | if (!isPointerTy(ArgType)) { | 
|  | CalledArgTys.push_back(nullptr); | 
|  | } else if (isTypedPointerTy(ArgType)) { | 
|  | CalledArgTys.push_back(cast<TypedPointerType>(ArgType)->getElementType()); | 
|  | HaveTypes = true; | 
|  | } else { | 
|  | Type *ElemTy = GR->findDeducedElementType(CalledArg); | 
|  | if (!ElemTy && hasPointeeTypeAttr(CalledArg)) | 
|  | ElemTy = getPointeeTypeByAttr(CalledArg); | 
|  | if (!ElemTy) { | 
|  | ElemTy = getPointeeTypeByCallInst(DemangledName, CalledF, OpIdx); | 
|  | if (ElemTy) { | 
|  | GR->addDeducedElementType(CalledArg, ElemTy); | 
|  | } else { | 
|  | for (User *U : CalledArg->users()) { | 
|  | if (Instruction *Inst = dyn_cast<Instruction>(U)) { | 
|  | if ((ElemTy = deduceElementTypeHelper(Inst)) != nullptr) | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | HaveTypes |= ElemTy != nullptr; | 
|  | CalledArgTys.push_back(ElemTy); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (DemangledName.empty() && !HaveTypes) | 
|  | return; | 
|  |  | 
|  | for (unsigned OpIdx = 0; OpIdx < CI->arg_size(); OpIdx++) { | 
|  | Value *ArgOperand = CI->getArgOperand(OpIdx); | 
|  | if (!isa<PointerType>(ArgOperand->getType()) && | 
|  | !isa<TypedPointerType>(ArgOperand->getType())) | 
|  | continue; | 
|  |  | 
|  | // Constants (nulls/undefs) are handled in insertAssignPtrTypeIntrs() | 
|  | if (!isa<Instruction>(ArgOperand) && !isa<Argument>(ArgOperand)) { | 
|  | // However, we may have assumptions about the formal argument's type and | 
|  | // may have a need to insert a ptr cast for the actual parameter of this | 
|  | // call. | 
|  | Argument *CalledArg = CalledF->getArg(OpIdx); | 
|  | if (!GR->findDeducedElementType(CalledArg)) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | Type *ExpectedType = | 
|  | OpIdx < CalledArgTys.size() ? CalledArgTys[OpIdx] : nullptr; | 
|  | if (!ExpectedType && !DemangledName.empty()) | 
|  | ExpectedType = SPIRV::parseBuiltinCallArgumentBaseType( | 
|  | DemangledName, OpIdx, I->getContext()); | 
|  | if (!ExpectedType) | 
|  | continue; | 
|  |  | 
|  | if (ExpectedType->isTargetExtTy()) | 
|  | insertAssignTypeInstrForTargetExtTypes(cast<TargetExtType>(ExpectedType), | 
|  | ArgOperand, B); | 
|  | else | 
|  | replacePointerOperandWithPtrCast(CI, ArgOperand, ExpectedType, OpIdx, B); | 
|  | } | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitInsertElementInst(InsertElementInst &I) { | 
|  | SmallVector<Type *, 4> Types = {I.getType(), I.getOperand(0)->getType(), | 
|  | I.getOperand(1)->getType(), | 
|  | I.getOperand(2)->getType()}; | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Value *> Args(I.op_begin(), I.op_end()); | 
|  | auto *NewI = B.CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args}); | 
|  | std::string InstName = I.hasName() ? I.getName().str() : ""; | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | NewI->setName(InstName); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction * | 
|  | SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &I) { | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Type *, 3> Types = {I.getType(), I.getVectorOperandType(), | 
|  | I.getIndexOperand()->getType()}; | 
|  | SmallVector<Value *, 2> Args = {I.getVectorOperand(), I.getIndexOperand()}; | 
|  | auto *NewI = B.CreateIntrinsic(Intrinsic::spv_extractelt, {Types}, {Args}); | 
|  | std::string InstName = I.hasName() ? I.getName().str() : ""; | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | NewI->setName(InstName); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &I) { | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Type *, 1> Types = {I.getInsertedValueOperand()->getType()}; | 
|  | SmallVector<Value *> Args; | 
|  | for (auto &Op : I.operands()) | 
|  | if (isa<UndefValue>(Op)) | 
|  | Args.push_back(UndefValue::get(B.getInt32Ty())); | 
|  | else | 
|  | Args.push_back(Op); | 
|  | for (auto &Op : I.indices()) | 
|  | Args.push_back(B.getInt32(Op)); | 
|  | Instruction *NewI = | 
|  | B.CreateIntrinsic(Intrinsic::spv_insertv, {Types}, {Args}); | 
|  | replaceMemInstrUses(&I, NewI, B); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitExtractValueInst(ExtractValueInst &I) { | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Value *> Args; | 
|  | for (auto &Op : I.operands()) | 
|  | Args.push_back(Op); | 
|  | for (auto &Op : I.indices()) | 
|  | Args.push_back(B.getInt32(Op)); | 
|  | auto *NewI = | 
|  | B.CreateIntrinsic(Intrinsic::spv_extractv, {I.getType()}, {Args}); | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitLoadInst(LoadInst &I) { | 
|  | if (!I.getType()->isAggregateType()) | 
|  | return &I; | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | TrackConstants = false; | 
|  | const auto *TLI = TM->getSubtargetImpl()->getTargetLowering(); | 
|  | MachineMemOperand::Flags Flags = | 
|  | TLI->getLoadMemOperandFlags(I, F->getParent()->getDataLayout()); | 
|  | auto *NewI = | 
|  | B.CreateIntrinsic(Intrinsic::spv_load, {I.getOperand(0)->getType()}, | 
|  | {I.getPointerOperand(), B.getInt16(Flags), | 
|  | B.getInt8(I.getAlign().value())}); | 
|  | replaceMemInstrUses(&I, NewI, B); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &I) { | 
|  | if (!AggrStores.contains(&I)) | 
|  | return &I; | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | TrackConstants = false; | 
|  | const auto *TLI = TM->getSubtargetImpl()->getTargetLowering(); | 
|  | MachineMemOperand::Flags Flags = | 
|  | TLI->getStoreMemOperandFlags(I, F->getParent()->getDataLayout()); | 
|  | auto *PtrOp = I.getPointerOperand(); | 
|  | auto *NewI = B.CreateIntrinsic( | 
|  | Intrinsic::spv_store, {I.getValueOperand()->getType(), PtrOp->getType()}, | 
|  | {I.getValueOperand(), PtrOp, B.getInt16(Flags), | 
|  | B.getInt8(I.getAlign().value())}); | 
|  | I.eraseFromParent(); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &I) { | 
|  | Value *ArraySize = nullptr; | 
|  | if (I.isArrayAllocation()) { | 
|  | const SPIRVSubtarget *STI = TM->getSubtargetImpl(*I.getFunction()); | 
|  | if (!STI->canUseExtension( | 
|  | SPIRV::Extension::SPV_INTEL_variable_length_array)) | 
|  | report_fatal_error( | 
|  | "array allocation: this instruction requires the following " | 
|  | "SPIR-V extension: SPV_INTEL_variable_length_array", | 
|  | false); | 
|  | ArraySize = I.getArraySize(); | 
|  | } | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | TrackConstants = false; | 
|  | Type *PtrTy = I.getType(); | 
|  | auto *NewI = | 
|  | ArraySize ? B.CreateIntrinsic(Intrinsic::spv_alloca_array, | 
|  | {PtrTy, ArraySize->getType()}, {ArraySize}) | 
|  | : B.CreateIntrinsic(Intrinsic::spv_alloca, {PtrTy}, {}); | 
|  | std::string InstName = I.hasName() ? I.getName().str() : ""; | 
|  | I.replaceAllUsesWith(NewI); | 
|  | I.eraseFromParent(); | 
|  | NewI->setName(InstName); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) { | 
|  | assert(I.getType()->isAggregateType() && "Aggregate result is expected"); | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | SmallVector<Value *> Args; | 
|  | for (auto &Op : I.operands()) | 
|  | Args.push_back(Op); | 
|  | Args.push_back(B.getInt32(I.getSyncScopeID())); | 
|  | Args.push_back(B.getInt32( | 
|  | static_cast<uint32_t>(getMemSemantics(I.getSuccessOrdering())))); | 
|  | Args.push_back(B.getInt32( | 
|  | static_cast<uint32_t>(getMemSemantics(I.getFailureOrdering())))); | 
|  | auto *NewI = B.CreateIntrinsic(Intrinsic::spv_cmpxchg, | 
|  | {I.getPointerOperand()->getType()}, {Args}); | 
|  | replaceMemInstrUses(&I, NewI, B); | 
|  | return NewI; | 
|  | } | 
|  |  | 
|  | Instruction *SPIRVEmitIntrinsics::visitUnreachableInst(UnreachableInst &I) { | 
|  | IRBuilder<> B(I.getParent()); | 
|  | B.SetInsertPoint(&I); | 
|  | B.CreateIntrinsic(Intrinsic::spv_unreachable, {}, {}); | 
|  | return &I; | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV, | 
|  | IRBuilder<> &B) { | 
|  | // Skip special artifical variable llvm.global.annotations. | 
|  | if (GV.getName() == "llvm.global.annotations") | 
|  | return; | 
|  | if (GV.hasInitializer() && !isa<UndefValue>(GV.getInitializer())) { | 
|  | // Deduce element type and store results in Global Registry. | 
|  | // Result is ignored, because TypedPointerType is not supported | 
|  | // by llvm IR general logic. | 
|  | deduceElementTypeHelper(&GV); | 
|  | Constant *Init = GV.getInitializer(); | 
|  | Type *Ty = isAggrToReplace(Init) ? B.getInt32Ty() : Init->getType(); | 
|  | Constant *Const = isAggrToReplace(Init) ? B.getInt32(1) : Init; | 
|  | auto *InitInst = B.CreateIntrinsic(Intrinsic::spv_init_global, | 
|  | {GV.getType(), Ty}, {&GV, Const}); | 
|  | InitInst->setArgOperand(1, Init); | 
|  | } | 
|  | if ((!GV.hasInitializer() || isa<UndefValue>(GV.getInitializer())) && | 
|  | GV.getNumUses() == 0) | 
|  | B.CreateIntrinsic(Intrinsic::spv_unref_global, GV.getType(), &GV); | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *I, | 
|  | IRBuilder<> &B) { | 
|  | reportFatalOnTokenType(I); | 
|  | if (!isPointerTy(I->getType()) || !requireAssignType(I) || | 
|  | isa<BitCastInst>(I)) | 
|  | return; | 
|  |  | 
|  | setInsertPointSkippingPhis(B, I->getNextNode()); | 
|  |  | 
|  | Type *ElemTy = deduceElementType(I); | 
|  | Constant *EltTyConst = UndefValue::get(ElemTy); | 
|  | unsigned AddressSpace = getPointerAddressSpace(I->getType()); | 
|  | CallInst *CI = buildIntrWithMD(Intrinsic::spv_assign_ptr_type, {I->getType()}, | 
|  | EltTyConst, I, {B.getInt32(AddressSpace)}, B); | 
|  | GR->addDeducedElementType(CI, ElemTy); | 
|  | AssignPtrTypeInstr[I] = CI; | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I, | 
|  | IRBuilder<> &B) { | 
|  | reportFatalOnTokenType(I); | 
|  | Type *Ty = I->getType(); | 
|  | if (!Ty->isVoidTy() && !isPointerTy(Ty) && requireAssignType(I)) { | 
|  | setInsertPointSkippingPhis(B, I->getNextNode()); | 
|  | Type *TypeToAssign = Ty; | 
|  | if (auto *II = dyn_cast<IntrinsicInst>(I)) { | 
|  | if (II->getIntrinsicID() == Intrinsic::spv_const_composite || | 
|  | II->getIntrinsicID() == Intrinsic::spv_undef) { | 
|  | auto It = AggrConstTypes.find(II); | 
|  | if (It == AggrConstTypes.end()) | 
|  | report_fatal_error("Unknown composite intrinsic type"); | 
|  | TypeToAssign = It->second; | 
|  | } | 
|  | } | 
|  | Constant *Const = UndefValue::get(TypeToAssign); | 
|  | buildIntrWithMD(Intrinsic::spv_assign_type, {Ty}, Const, I, {}, B); | 
|  | } | 
|  | for (const auto &Op : I->operands()) { | 
|  | if (isa<ConstantPointerNull>(Op) || isa<UndefValue>(Op) || | 
|  | // Check GetElementPtrConstantExpr case. | 
|  | (isa<ConstantExpr>(Op) && isa<GEPOperator>(Op))) { | 
|  | setInsertPointSkippingPhis(B, I); | 
|  | if (isa<UndefValue>(Op) && Op->getType()->isAggregateType()) | 
|  | buildIntrWithMD(Intrinsic::spv_assign_type, {B.getInt32Ty()}, Op, | 
|  | UndefValue::get(B.getInt32Ty()), {}, B); | 
|  | else if (!isa<Instruction>(Op)) // TODO: This case could be removed | 
|  | buildIntrWithMD(Intrinsic::spv_assign_type, {Op->getType()}, Op, Op, {}, | 
|  | B); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I, | 
|  | IRBuilder<> &B) { | 
|  | auto *II = dyn_cast<IntrinsicInst>(I); | 
|  | if (II && II->getIntrinsicID() == Intrinsic::spv_const_composite && | 
|  | TrackConstants) { | 
|  | B.SetInsertPoint(I->getNextNode()); | 
|  | Type *Ty = B.getInt32Ty(); | 
|  | auto t = AggrConsts.find(I); | 
|  | assert(t != AggrConsts.end()); | 
|  | auto *NewOp = buildIntrWithMD(Intrinsic::spv_track_constant, {Ty, Ty}, | 
|  | t->second, I, {}, B); | 
|  | I->replaceAllUsesWith(NewOp); | 
|  | NewOp->setArgOperand(0, I); | 
|  | } | 
|  | for (const auto &Op : I->operands()) { | 
|  | if ((isa<ConstantAggregateZero>(Op) && Op->getType()->isVectorTy()) || | 
|  | isa<PHINode>(I) || isa<SwitchInst>(I)) | 
|  | TrackConstants = false; | 
|  | if ((isa<ConstantData>(Op) || isa<ConstantExpr>(Op)) && TrackConstants) { | 
|  | unsigned OpNo = Op.getOperandNo(); | 
|  | if (II && ((II->getIntrinsicID() == Intrinsic::spv_gep && OpNo == 0) || | 
|  | (II->paramHasAttr(OpNo, Attribute::ImmArg)))) | 
|  | continue; | 
|  | B.SetInsertPoint(I); | 
|  | Value *OpTyVal = Op; | 
|  | if (Op->getType()->isTargetExtTy()) | 
|  | OpTyVal = Constant::getNullValue( | 
|  | IntegerType::get(I->getContext(), GR->getPointerSize())); | 
|  | auto *NewOp = buildIntrWithMD(Intrinsic::spv_track_constant, | 
|  | {Op->getType(), OpTyVal->getType()}, Op, | 
|  | OpTyVal, {}, B); | 
|  | I->setOperand(OpNo, NewOp); | 
|  | } | 
|  | } | 
|  | if (I->hasName()) { | 
|  | reportFatalOnTokenType(I); | 
|  | setInsertPointSkippingPhis(B, I->getNextNode()); | 
|  | std::vector<Value *> Args = {I}; | 
|  | addStringImm(I->getName(), B, Args); | 
|  | B.CreateIntrinsic(Intrinsic::spv_assign_name, {I->getType()}, Args); | 
|  | } | 
|  | } | 
|  |  | 
|  | Type *SPIRVEmitIntrinsics::deduceFunParamElementType(Function *F, | 
|  | unsigned OpIdx) { | 
|  | std::unordered_set<Function *> FVisited; | 
|  | return deduceFunParamElementType(F, OpIdx, FVisited); | 
|  | } | 
|  |  | 
|  | Type *SPIRVEmitIntrinsics::deduceFunParamElementType( | 
|  | Function *F, unsigned OpIdx, std::unordered_set<Function *> &FVisited) { | 
|  | // maybe a cycle | 
|  | if (FVisited.find(F) != FVisited.end()) | 
|  | return nullptr; | 
|  | FVisited.insert(F); | 
|  |  | 
|  | std::unordered_set<Value *> Visited; | 
|  | SmallVector<std::pair<Function *, unsigned>> Lookup; | 
|  | // search in function's call sites | 
|  | for (User *U : F->users()) { | 
|  | CallInst *CI = dyn_cast<CallInst>(U); | 
|  | if (!CI || OpIdx >= CI->arg_size()) | 
|  | continue; | 
|  | Value *OpArg = CI->getArgOperand(OpIdx); | 
|  | if (!isPointerTy(OpArg->getType())) | 
|  | continue; | 
|  | // maybe we already know operand's element type | 
|  | if (Type *KnownTy = GR->findDeducedElementType(OpArg)) | 
|  | return KnownTy; | 
|  | // try to deduce from the operand itself | 
|  | Visited.clear(); | 
|  | if (Type *Ty = deduceElementTypeHelper(OpArg, Visited)) | 
|  | return Ty; | 
|  | // search in actual parameter's users | 
|  | for (User *OpU : OpArg->users()) { | 
|  | Instruction *Inst = dyn_cast<Instruction>(OpU); | 
|  | if (!Inst || Inst == CI) | 
|  | continue; | 
|  | Visited.clear(); | 
|  | if (Type *Ty = deduceElementTypeHelper(Inst, Visited)) | 
|  | return Ty; | 
|  | } | 
|  | // check if it's a formal parameter of the outer function | 
|  | if (!CI->getParent() || !CI->getParent()->getParent()) | 
|  | continue; | 
|  | Function *OuterF = CI->getParent()->getParent(); | 
|  | if (FVisited.find(OuterF) != FVisited.end()) | 
|  | continue; | 
|  | for (unsigned i = 0; i < OuterF->arg_size(); ++i) { | 
|  | if (OuterF->getArg(i) == OpArg) { | 
|  | Lookup.push_back(std::make_pair(OuterF, i)); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // search in function parameters | 
|  | for (auto &Pair : Lookup) { | 
|  | if (Type *Ty = deduceFunParamElementType(Pair.first, Pair.second, FVisited)) | 
|  | return Ty; | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::processParamTypesByFunHeader(Function *F, | 
|  | IRBuilder<> &B) { | 
|  | B.SetInsertPointPastAllocas(F); | 
|  | for (unsigned OpIdx = 0; OpIdx < F->arg_size(); ++OpIdx) { | 
|  | Argument *Arg = F->getArg(OpIdx); | 
|  | if (!isUntypedPointerTy(Arg->getType())) | 
|  | continue; | 
|  | Type *ElemTy = GR->findDeducedElementType(Arg); | 
|  | if (!ElemTy && hasPointeeTypeAttr(Arg) && | 
|  | (ElemTy = getPointeeTypeByAttr(Arg)) != nullptr) | 
|  | buildAssignPtr(B, ElemTy, Arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SPIRVEmitIntrinsics::processParamTypes(Function *F, IRBuilder<> &B) { | 
|  | B.SetInsertPointPastAllocas(F); | 
|  | for (unsigned OpIdx = 0; OpIdx < F->arg_size(); ++OpIdx) { | 
|  | Argument *Arg = F->getArg(OpIdx); | 
|  | if (!isUntypedPointerTy(Arg->getType())) | 
|  | continue; | 
|  | Type *ElemTy = GR->findDeducedElementType(Arg); | 
|  | if (!ElemTy && (ElemTy = deduceFunParamElementType(F, OpIdx)) != nullptr) | 
|  | buildAssignPtr(B, ElemTy, Arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) { | 
|  | if (Func.isDeclaration()) | 
|  | return false; | 
|  |  | 
|  | const SPIRVSubtarget &ST = TM->getSubtarget<SPIRVSubtarget>(Func); | 
|  | GR = ST.getSPIRVGlobalRegistry(); | 
|  |  | 
|  | F = &Func; | 
|  | IRBuilder<> B(Func.getContext()); | 
|  | AggrConsts.clear(); | 
|  | AggrConstTypes.clear(); | 
|  | AggrStores.clear(); | 
|  |  | 
|  | processParamTypesByFunHeader(F, B); | 
|  |  | 
|  | // StoreInst's operand type can be changed during the next transformations, | 
|  | // so we need to store it in the set. Also store already transformed types. | 
|  | for (auto &I : instructions(Func)) { | 
|  | StoreInst *SI = dyn_cast<StoreInst>(&I); | 
|  | if (!SI) | 
|  | continue; | 
|  | Type *ElTy = SI->getValueOperand()->getType(); | 
|  | if (ElTy->isAggregateType() || ElTy->isVectorTy()) | 
|  | AggrStores.insert(&I); | 
|  | } | 
|  |  | 
|  | B.SetInsertPoint(&Func.getEntryBlock(), Func.getEntryBlock().begin()); | 
|  | for (auto &GV : Func.getParent()->globals()) | 
|  | processGlobalValue(GV, B); | 
|  |  | 
|  | preprocessUndefs(B); | 
|  | preprocessCompositeConstants(B); | 
|  | SmallVector<Instruction *> Worklist; | 
|  | for (auto &I : instructions(Func)) | 
|  | Worklist.push_back(&I); | 
|  |  | 
|  | for (auto &I : Worklist) { | 
|  | insertAssignPtrTypeIntrs(I, B); | 
|  | insertAssignTypeIntrs(I, B); | 
|  | insertPtrCastOrAssignTypeInstr(I, B); | 
|  | } | 
|  |  | 
|  | for (auto &I : instructions(Func)) | 
|  | deduceOperandElementType(&I); | 
|  |  | 
|  | for (auto *I : Worklist) { | 
|  | TrackConstants = true; | 
|  | if (!I->getType()->isVoidTy() || isa<StoreInst>(I)) | 
|  | B.SetInsertPoint(I->getNextNode()); | 
|  | // Visitors return either the original/newly created instruction for further | 
|  | // processing, nullptr otherwise. | 
|  | I = visit(*I); | 
|  | if (!I) | 
|  | continue; | 
|  | processInstrAfterVisit(I, B); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool SPIRVEmitIntrinsics::runOnModule(Module &M) { | 
|  | bool Changed = false; | 
|  |  | 
|  | for (auto &F : M) { | 
|  | Changed |= runOnFunction(F); | 
|  | } | 
|  |  | 
|  | for (auto &F : M) { | 
|  | // check if function parameter types are set | 
|  | if (!F.isDeclaration() && !F.isIntrinsic()) { | 
|  | const SPIRVSubtarget &ST = TM->getSubtarget<SPIRVSubtarget>(F); | 
|  | GR = ST.getSPIRVGlobalRegistry(); | 
|  | IRBuilder<> B(F.getContext()); | 
|  | processParamTypes(&F, B); | 
|  | } | 
|  | } | 
|  |  | 
|  | return Changed; | 
|  | } | 
|  |  | 
|  | ModulePass *llvm::createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM) { | 
|  | return new SPIRVEmitIntrinsics(TM); | 
|  | } |