//===- SPIRVUtil.cpp - SPIR-V Utilities -------------------------*- C++ -*-===// // // The LLVM/SPIRV Translator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // // Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal with the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimers in the documentation // and/or other materials provided with the distribution. // Neither the names of Advanced Micro Devices, Inc., nor the names of its // contributors may be used to endorse or promote products derived from this // Software without specific prior written permission. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // //===----------------------------------------------------------------------===// /// \file /// /// This file defines utility classes and functions shared by SPIR-V /// reader/writer. /// //===----------------------------------------------------------------------===// #include "SPIRVInternal.h" #include "libSPIRV/SPIRVDecorate.h" #include "libSPIRV/SPIRVValue.h" #include "SPIRVMDWalker.h" #include "OCLUtil.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" #include #include #define DEBUG_TYPE "spirv" namespace SPIRV{ #ifdef _SPIRV_SUPPORT_TEXT_FMT cl::opt UseTextFormat("spirv-text", cl::desc("Use text format for SPIR-V for debugging purpose"), cl::location(SPIRVUseTextFormat)); #endif #ifdef _SPIRVDBG cl::opt EnableDbgOutput("spirv-debug", cl::desc("Enable SPIR-V debug output"), cl::location(SPIRVDbgEnable)); #endif void addFnAttr(LLVMContext *Context, CallInst *Call, Attribute::AttrKind Attr) { Call->addAttribute(AttributeSet::FunctionIndex, Attr); } void removeFnAttr(LLVMContext *Context, CallInst *Call, Attribute::AttrKind Attr) { Call->removeAttribute(AttributeSet::FunctionIndex, Attribute::get(*Context, Attr)); } Value * removeCast(Value *V) { auto Cast = dyn_cast(V); if (Cast && Cast->isCast()) { return removeCast(Cast->getOperand(0)); } if (auto Cast = dyn_cast(V)) return removeCast(Cast->getOperand(0)); return V; } void saveLLVMModule(Module *M, const std::string &OutputFile) { std::error_code EC; tool_output_file Out(OutputFile.c_str(), EC, sys::fs::F_None); if (EC) { SPIRVDBG(errs() << "Fails to open output file: " << EC.message();) return; } WriteBitcodeToFile(M, Out.os()); Out.keep(); } std::string mapLLVMTypeToOCLType(const Type* Ty, bool Signed) { if (Ty->isHalfTy()) return "half"; if (Ty->isFloatTy()) return "float"; if (Ty->isDoubleTy()) return "double"; if (auto intTy = dyn_cast(Ty)) { std::string SignPrefix; std::string Stem; if (!Signed) SignPrefix = "u"; switch (intTy->getIntegerBitWidth()) { case 8: Stem = "char"; break; case 16: Stem = "short"; break; case 32: Stem = "int"; break; case 64: Stem = "long"; break; default: Stem = "invalid_type"; break; } return SignPrefix + Stem; } if (auto vecTy = dyn_cast(Ty)) { Type* eleTy = vecTy->getElementType(); unsigned size = vecTy->getVectorNumElements(); std::stringstream ss; ss << mapLLVMTypeToOCLType(eleTy, Signed) << size; return ss.str(); } return "invalid_type"; } std::string mapSPIRVTypeToOCLType(SPIRVType* Ty, bool Signed) { if (Ty->isTypeFloat()) { auto W = Ty->getBitWidth(); switch (W) { case 16: return "half"; case 32: return "float"; case 64: return "double"; default: assert (0 && "Invalid floating pointer type"); return std::string("float") + W + "_t"; } } if (Ty->isTypeInt()) { std::string SignPrefix; std::string Stem; if (!Signed) SignPrefix = "u"; auto W = Ty->getBitWidth(); switch (W) { case 8: Stem = "char"; break; case 16: Stem = "short"; break; case 32: Stem = "int"; break; case 64: Stem = "long"; break; default: llvm_unreachable("Invalid integer type"); Stem = std::string("int") + W + "_t"; break; } return SignPrefix + Stem; } if (Ty->isTypeVector()) { auto eleTy = Ty->getVectorComponentType(); auto size = Ty->getVectorComponentCount(); std::stringstream ss; ss << mapSPIRVTypeToOCLType(eleTy, Signed) << size; return ss.str(); } llvm_unreachable("Invalid type"); return "unknown_type"; } PointerType* getOrCreateOpaquePtrType(Module *M, const std::string &Name, unsigned AddrSpace) { auto OpaqueType = M->getTypeByName(Name); if (!OpaqueType) OpaqueType = StructType::create(M->getContext(), Name); return PointerType::get(OpaqueType, AddrSpace); } PointerType* getSamplerType(Module *M) { return getOrCreateOpaquePtrType(M, getSPIRVTypeName(kSPIRVTypeName::Sampler), SPIRAS_Constant); } PointerType* getPipeStorageType(Module* M) { return getOrCreateOpaquePtrType(M, getSPIRVTypeName( kSPIRVTypeName::PipeStorage), SPIRAS_Constant); } void getFunctionTypeParameterTypes(llvm::FunctionType* FT, std::vector& ArgTys) { for (auto I = FT->param_begin(), E = FT->param_end(); I != E; ++I) { ArgTys.push_back(*I); } } bool isVoidFuncTy(FunctionType *FT) { return FT->getReturnType()->isVoidTy() && FT->getNumParams() == 0; } bool isPointerToOpaqueStructType(llvm::Type* Ty) { if (auto PT = dyn_cast(Ty)) if (auto ST = dyn_cast(PT->getElementType())) if (ST->isOpaque()) return true; return false; } bool isPointerToOpaqueStructType(llvm::Type* Ty, const std::string &Name) { if (auto PT = dyn_cast(Ty)) if (auto ST = dyn_cast(PT->getElementType())) if (ST->isOpaque() && ST->getName() == Name) return true; return false; } bool isOCLImageType(llvm::Type* Ty, StringRef *Name) { if (auto PT = dyn_cast(Ty)) if (auto ST = dyn_cast(PT->getElementType())) if (ST->isOpaque()) { auto FullName = ST->getName(); if (FullName.find(kSPR2TypeName::ImagePrefix) == 0) { if (Name) *Name = FullName.drop_front(strlen(kSPR2TypeName::OCLPrefix)); return true; } } return false; } /// \param BaseTyName is the type name as in spirv.BaseTyName.Postfixes /// \param Postfix contains postfixes extracted from the SPIR-V image /// type name as spirv.BaseTyName.Postfixes. bool isSPIRVType(llvm::Type* Ty, StringRef BaseTyName, StringRef *Postfix) { if (auto PT = dyn_cast(Ty)) if (auto ST = dyn_cast(PT->getElementType())) if (ST->isOpaque()) { auto FullName = ST->getName(); std::string N = std::string(kSPIRVTypeName::PrefixAndDelim) + BaseTyName.str(); if (FullName != N) N = N + kSPIRVTypeName::Delimiter; if (FullName.startswith(N)) { if (Postfix) *Postfix = FullName.drop_front(N.size()); return true; } } return false; } Function * getOrCreateFunction(Module *M, Type *RetTy, ArrayRef ArgTypes, StringRef Name, BuiltinFuncMangleInfo *Mangle, AttributeSet *Attrs, bool takeName) { std::string MangledName = Name; bool isVarArg = false; if (Mangle) { MangledName = mangleBuiltin(Name, ArgTypes, Mangle); isVarArg = 0 <= Mangle->getVarArg(); if(isVarArg) ArgTypes = ArgTypes.slice(0, Mangle->getVarArg()); } FunctionType *FT = FunctionType::get(RetTy, ArgTypes, isVarArg); Function *F = M->getFunction(MangledName); if (!takeName && F && F->getFunctionType() != FT && Mangle != nullptr) { std::string S; raw_string_ostream SS(S); SS << "Error: Attempt to redefine function: " << *F << " => " << *FT << '\n'; report_fatal_error(SS.str(), false); } if (!F || F->getFunctionType() != FT) { auto NewF = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); if (F && takeName) { NewF->takeName(F); DEBUG(dbgs() << "[getOrCreateFunction] Warning: taking function name\n"); } if (NewF->getName() != MangledName) { DEBUG(dbgs() << "[getOrCreateFunction] Warning: function name changed\n"); } DEBUG(dbgs() << "[getOrCreateFunction] "; if (F) dbgs() << *F << " => "; dbgs() << *NewF << '\n'; ); F = NewF; F->setCallingConv(CallingConv::SPIR_FUNC); if (Attrs) F->setAttributes(*Attrs); } return F; } std::vector getArguments(CallInst* CI, unsigned Start, unsigned End) { std::vector Args; if (End == 0) End = CI->getNumArgOperands(); for (; Start != End; ++Start) { Args.push_back(CI->getArgOperand(Start)); } return Args; } uint64_t getArgAsInt(CallInst *CI, unsigned I){ return cast(CI->getArgOperand(I))->getZExtValue(); } Scope getArgAsScope(CallInst *CI, unsigned I){ return static_cast(getArgAsInt(CI, I)); } Decoration getArgAsDecoration(CallInst *CI, unsigned I) { return static_cast(getArgAsInt(CI, I)); } std::string decorateSPIRVFunction(const std::string &S) { return std::string(kSPIRVName::Prefix) + S + kSPIRVName::Postfix; } std::string undecorateSPIRVFunction(const std::string& S) { assert (S.find(kSPIRVName::Prefix) == 0); const size_t Start = strlen(kSPIRVName::Prefix); auto End = S.rfind(kSPIRVName::Postfix); return S.substr(Start, End - Start); } std::string prefixSPIRVName(const std::string &S) { return std::string(kSPIRVName::Prefix) + S; } StringRef dePrefixSPIRVName(StringRef R, SmallVectorImpl &Postfix) { const size_t Start = strlen(kSPIRVName::Prefix); if (!R.startswith(kSPIRVName::Prefix)) return R; R = R.drop_front(Start); R.split(Postfix, "_", -1, false); auto Name = Postfix.front(); Postfix.erase(Postfix.begin()); return Name; } std::string getSPIRVFuncName(Op OC, StringRef PostFix) { return prefixSPIRVName(getName(OC) + PostFix.str()); } std::string getSPIRVFuncName(Op OC, const Type *pRetTy, bool IsSigned) { return prefixSPIRVName(getName(OC) + kSPIRVPostfix::Divider + getPostfixForReturnType(pRetTy, false)); } std::string getSPIRVExtFuncName(SPIRVExtInstSetKind Set, unsigned ExtOp, StringRef PostFix) { std::string ExtOpName; switch(Set) { default: llvm_unreachable("invalid extended instruction set"); ExtOpName = "unknown"; break; case SPIRVEIS_OpenCL: ExtOpName = getName(static_cast(ExtOp)); break; } return prefixSPIRVName(SPIRVExtSetShortNameMap::map(Set) + '_' + ExtOpName + PostFix.str()); } SPIRVDecorate * mapPostfixToDecorate(StringRef Postfix, SPIRVEntry *Target) { if (Postfix == kSPIRVPostfix::Sat) return new SPIRVDecorate(spv::DecorationSaturatedConversion, Target); if (Postfix.startswith(kSPIRVPostfix::Rt)) return new SPIRVDecorate(spv::DecorationFPRoundingMode, Target, map(Postfix.str())); return nullptr; } SPIRVValue * addDecorations(SPIRVValue *Target, const SmallVectorImpl& Decs){ for (auto &I:Decs) if (auto Dec = mapPostfixToDecorate(I, Target)) Target->addDecorate(Dec); return Target; } std::string getPostfix(Decoration Dec, unsigned Value) { switch(Dec) { default: llvm_unreachable("not implemented"); return "unknown"; case spv::DecorationSaturatedConversion: return kSPIRVPostfix::Sat; case spv::DecorationFPRoundingMode: return rmap(static_cast(Value)); } } std::string getPostfixForReturnType(CallInst *CI, bool IsSigned) { return getPostfixForReturnType(CI->getType(), IsSigned); } std::string getPostfixForReturnType(const Type *pRetTy, bool IsSigned) { return std::string(kSPIRVPostfix::Return) + mapLLVMTypeToOCLType(pRetTy, IsSigned); } Op getSPIRVFuncOC(const std::string& S, SmallVectorImpl *Dec) { Op OC; SmallVector Postfix; std::string Name; if (!oclIsBuiltin(S, &Name)) Name = S; StringRef R(Name); R = dePrefixSPIRVName(R, Postfix); if (!getByName(R.str(), OC)) return OpNop; if (Dec) for (auto &I:Postfix) Dec->push_back(I.str()); return OC; } bool getSPIRVBuiltin(const std::string &OrigName, spv::BuiltIn &B) { SmallVector Postfix; StringRef R(OrigName); R = dePrefixSPIRVName(R, Postfix); assert(Postfix.empty() && "Invalid SPIR-V builtin name"); return getByName(R.str(), B); } bool oclIsBuiltin(const StringRef &Name, std::string *DemangledName, bool isCPP) { if (Name == "printf") { if (DemangledName) *DemangledName = Name; return true; } if (!Name.startswith("_Z")) return false; if (!DemangledName) return true; // OpenCL C++ built-ins are declared in cl namespace. // TODO: consider using 'St' abbriviation for cl namespace mangling. // Similar to ::std:: in C++. if (isCPP) { if (!Name.startswith("_ZN")) return false; // Skip CV and ref qualifiers. size_t NameSpaceStart = Name.find_first_not_of("rVKRO", 3); // All built-ins are in the ::cl:: namespace. if (Name.substr(NameSpaceStart, 11) != "2cl7__spirv") return false; size_t DemangledNameLenStart = NameSpaceStart + 11; size_t Start = Name.find_first_not_of("0123456789", DemangledNameLenStart); size_t Len = 0; Name.substr(DemangledNameLenStart, Start - DemangledNameLenStart) .getAsInteger(10, Len); *DemangledName = Name.substr(Start, Len); } else { size_t Start = Name.find_first_not_of("0123456789", 2); size_t Len = 0; Name.substr(2, Start - 2).getAsInteger(10, Len); *DemangledName = Name.substr(Start, Len); } return true; } // Check if a mangled type name is unsigned bool isMangledTypeUnsigned(char Mangled) { return Mangled == 'h' /* uchar */ || Mangled == 't' /* ushort */ || Mangled == 'j' /* uint */ || Mangled == 'm' /* ulong */; } // Check if a mangled type name is signed bool isMangledTypeSigned(char Mangled) { return Mangled == 'c' /* char */ || Mangled == 'a' /* signed char */ || Mangled == 's' /* short */ || Mangled == 'i' /* int */ || Mangled == 'l' /* long */; } // Check if a mangled type name is floating point (excludes half) bool isMangledTypeFP(char Mangled) { return Mangled == 'f' /* float */ || Mangled == 'd'; /* double */ } // Check if a mangled type name is half bool isMangledTypeHalf(std::string Mangled) { return Mangled == "Dh"; /* half */ } void eraseSubstitutionFromMangledName(std::string& MangledName) { auto Len = MangledName.length(); while (Len >= 2 && MangledName.substr(Len - 2, 2) == "S_") { Len -= 2; MangledName.erase(Len, 2); } } ParamType LastFuncParamType(const std::string &MangledName) { auto Copy = MangledName; eraseSubstitutionFromMangledName(Copy); char Mangled = Copy.back(); std::string Mangled2 = Copy.substr(Copy.size() - 2); if (isMangledTypeFP(Mangled) || isMangledTypeHalf(Mangled2)) { return ParamType::FLOAT; } else if (isMangledTypeUnsigned(Mangled)) { return ParamType::UNSIGNED; } else if (isMangledTypeSigned(Mangled)) { return ParamType::SIGNED; } return ParamType::UNKNOWN; } // Check if the last argument is signed bool isLastFuncParamSigned(const std::string& MangledName) { return LastFuncParamType(MangledName) == ParamType::SIGNED; } // Check if a mangled function name contains unsigned atomic type bool containsUnsignedAtomicType(StringRef Name) { auto Loc = Name.find(kMangledName::AtomicPrefixIncoming); if (Loc == StringRef::npos) return false; return isMangledTypeUnsigned(Name[Loc + strlen( kMangledName::AtomicPrefixIncoming)]); } bool isFunctionPointerType(Type *T) { if (isa(T) && isa(T->getPointerElementType())) { return true; } return false; } bool hasFunctionPointerArg(Function *F, Function::arg_iterator& AI) { AI = F->arg_begin(); for (auto AE = F->arg_end(); AI != AE; ++AI) { DEBUG(dbgs() << "[hasFuncPointerArg] " << *AI << '\n'); if (isFunctionPointerType(AI->getType())) { return true; } } return false; } Constant * castToVoidFuncPtr(Function *F) { auto T = getVoidFuncPtrType(F->getParent()); return ConstantExpr::getBitCast(F, T); } bool hasArrayArg(Function *F) { for (auto I = F->arg_begin(), E = F->arg_end(); I != E; ++I) { DEBUG(dbgs() << "[hasArrayArg] " << *I << '\n'); if (I->getType()->isArrayTy()) { return true; } } return false; } CallInst * mutateCallInst(Module *M, CallInst *CI, std::function &)>ArgMutate, BuiltinFuncMangleInfo *Mangle, AttributeSet *Attrs, bool TakeFuncName) { DEBUG(dbgs() << "[mutateCallInst] " << *CI); auto Args = getArguments(CI); auto NewName = ArgMutate(CI, Args); std::string InstName; if (!CI->getType()->isVoidTy() && CI->hasName()) { InstName = CI->getName(); CI->setName(InstName + ".old"); } auto NewCI = addCallInst(M, NewName, CI->getType(), Args, Attrs, CI, Mangle, InstName, TakeFuncName); DEBUG(dbgs() << " => " << *NewCI << '\n'); CI->replaceAllUsesWith(NewCI); CI->dropAllReferences(); CI->removeFromParent(); return NewCI; } Instruction * mutateCallInst(Module *M, CallInst *CI, std::function &, Type *&RetTy)>ArgMutate, std::function RetMutate, BuiltinFuncMangleInfo *Mangle, AttributeSet *Attrs, bool TakeFuncName) { DEBUG(dbgs() << "[mutateCallInst] " << *CI); auto Args = getArguments(CI); Type *RetTy = CI->getType(); auto NewName = ArgMutate(CI, Args, RetTy); std::string InstName; if (CI->hasName()) { InstName = CI->getName(); CI->setName(InstName + ".old"); } auto NewCI = addCallInst(M, NewName, RetTy, Args, Attrs, CI, Mangle, InstName + ".tmp", TakeFuncName); auto NewI = RetMutate(NewCI); NewI->takeName(CI); DEBUG(dbgs() << " => " << *NewI << '\n'); CI->replaceAllUsesWith(NewI); CI->dropAllReferences(); CI->removeFromParent(); return NewI; } void mutateFunction(Function *F, std::function &)>ArgMutate, BuiltinFuncMangleInfo *Mangle, AttributeSet *Attrs, bool TakeFuncName) { auto M = F->getParent(); for (auto I = F->user_begin(), E = F->user_end(); I != E;) { if (auto CI = dyn_cast(*I++)) mutateCallInst(M, CI, ArgMutate, Mangle, Attrs, TakeFuncName); } if (F->use_empty()) F->eraseFromParent(); } CallInst * mutateCallInstSPIRV(Module *M, CallInst *CI, std::function &)>ArgMutate, AttributeSet *Attrs) { BuiltinFuncMangleInfo BtnInfo; return mutateCallInst(M, CI, ArgMutate, &BtnInfo, Attrs); } Instruction * mutateCallInstSPIRV(Module *M, CallInst *CI, std::function &, Type *&RetTy)> ArgMutate, std::function RetMutate, AttributeSet *Attrs) { BuiltinFuncMangleInfo BtnInfo; return mutateCallInst(M, CI, ArgMutate, RetMutate, &BtnInfo, Attrs); } CallInst * addCallInst(Module *M, StringRef FuncName, Type *RetTy, ArrayRef Args, AttributeSet *Attrs, Instruction *Pos, BuiltinFuncMangleInfo *Mangle, StringRef InstName, bool TakeFuncName) { auto F = getOrCreateFunction(M, RetTy, getTypes(Args), FuncName, Mangle, Attrs, TakeFuncName); // Cannot assign a name to void typed values auto CI = CallInst::Create(F, Args, RetTy->isVoidTy() ? "" : InstName, Pos); CI->setCallingConv(F->getCallingConv()); return CI; } CallInst * addCallInstSPIRV(Module *M, StringRef FuncName, Type *RetTy, ArrayRef Args, AttributeSet *Attrs, Instruction *Pos, StringRef InstName) { BuiltinFuncMangleInfo BtnInfo; return addCallInst(M, FuncName, RetTy, Args, Attrs, Pos, &BtnInfo, InstName); } bool isValidVectorSize(unsigned I) { return I == 2 || I == 3 || I == 4 || I == 8 || I == 16; } Value * addVector(Instruction *InsPos, ValueVecRange Range) { size_t VecSize = Range.second - Range.first; if (VecSize == 1) return *Range.first; assert(isValidVectorSize(VecSize) && "Invalid vector size"); IRBuilder<> Builder(InsPos); auto Vec = Builder.CreateVectorSplat(VecSize, *Range.first); unsigned Index = 1; for (++Range.first; Range.first != Range.second; ++Range.first, ++Index) Vec = Builder.CreateInsertElement(Vec, *Range.first, ConstantInt::get(Type::getInt32Ty(InsPos->getContext()), Index, false)); return Vec; } void makeVector(Instruction *InsPos, std::vector &Ops, ValueVecRange Range) { auto Vec = addVector(InsPos, Range); Ops.erase(Range.first, Range.second); Ops.push_back(Vec); } void expandVector(Instruction *InsPos, std::vector &Ops, size_t VecPos) { auto Vec = Ops[VecPos]; auto VT = Vec->getType(); if (!VT->isVectorTy()) return; size_t N = VT->getVectorNumElements(); IRBuilder<> Builder(InsPos); for (size_t I = 0; I != N; ++I) Ops.insert(Ops.begin() + VecPos + I, Builder.CreateExtractElement(Vec, ConstantInt::get(Type::getInt32Ty(InsPos->getContext()), I, false))); Ops.erase(Ops.begin() + VecPos + N); } Constant * castToInt8Ptr(Constant *V, unsigned Addr = 0) { return ConstantExpr::getBitCast(V, Type::getInt8PtrTy(V->getContext(), Addr)); } PointerType * getInt8PtrTy(PointerType *T) { return Type::getInt8PtrTy(T->getContext(), T->getAddressSpace()); } Value * castToInt8Ptr(Value *V, Instruction *Pos) { return CastInst::CreatePointerCast(V, getInt8PtrTy( cast(V->getType())), "", Pos); } CallInst * addBlockBind(Module *M, Function *InvokeFunc, Value *BlkCtx, Value *CtxLen, Value *CtxAlign, Instruction *InsPos, StringRef InstName) { auto BlkTy = getOrCreateOpaquePtrType(M, SPIR_TYPE_NAME_BLOCK_T, SPIRAS_Private); auto &Ctx = M->getContext(); Value *BlkArgs[] = { castToInt8Ptr(InvokeFunc), CtxLen ? CtxLen : UndefValue::get(Type::getInt32Ty(Ctx)), CtxAlign ? CtxAlign : UndefValue::get(Type::getInt32Ty(Ctx)), BlkCtx ? BlkCtx : UndefValue::get(Type::getInt8PtrTy(Ctx)) }; return addCallInst(M, SPIR_INTRINSIC_BLOCK_BIND, BlkTy, BlkArgs, nullptr, InsPos, nullptr, InstName); } IntegerType* getSizetType(Module *M) { return IntegerType::getIntNTy(M->getContext(), M->getDataLayout().getPointerSizeInBits(0)); } Type * getVoidFuncType(Module *M) { return FunctionType::get(Type::getVoidTy(M->getContext()), false); } Type * getVoidFuncPtrType(Module *M, unsigned AddrSpace) { return PointerType::get(getVoidFuncType(M), AddrSpace); } ConstantInt * getInt64(Module *M, int64_t value) { return ConstantInt::get(Type::getInt64Ty(M->getContext()), value, true); } Constant *getFloat32(Module *M, float value) { return ConstantFP::get(Type::getFloatTy(M->getContext()), value); } ConstantInt * getInt32(Module *M, int value) { return ConstantInt::get(Type::getInt32Ty(M->getContext()), value, true); } ConstantInt * getUInt32(Module *M, unsigned value) { return ConstantInt::get(Type::getInt32Ty(M->getContext()), value, false); } ConstantInt * getUInt16(Module *M, unsigned short value) { return ConstantInt::get(Type::getInt16Ty(M->getContext()), value, false); } std::vector getInt32(Module *M, const std::vector &value) { std::vector V; for (auto &I:value) V.push_back(getInt32(M, I)); return V; } ConstantInt * getSizet(Module *M, uint64_t value) { return ConstantInt::get(getSizetType(M), value, false); } /////////////////////////////////////////////////////////////////////////////// // // Functions for getting metadata // /////////////////////////////////////////////////////////////////////////////// int getMDOperandAsInt(MDNode* N, unsigned I) { return mdconst::dyn_extract(N->getOperand(I))->getZExtValue(); } std::string getMDOperandAsString(MDNode* N, unsigned I) { if (!N) return ""; Metadata* Op = N->getOperand(I); if (!Op) return ""; if (MDString* Str = dyn_cast(Op)) { return Str->getString().str(); } return ""; } Type* getMDOperandAsType(MDNode* N, unsigned I) { return cast(N->getOperand(I))->getType(); } std::set getNamedMDAsStringSet(Module *M, const std::string &MDName) { NamedMDNode *NamedMD = M->getNamedMetadata(MDName); std::set StrSet; if (!NamedMD) return StrSet; assert(NamedMD->getNumOperands() > 0 && "Invalid SPIR"); for (unsigned I = 0, E = NamedMD->getNumOperands(); I != E; ++I) { MDNode *MD = NamedMD->getOperand(I); if (!MD || MD->getNumOperands() == 0) continue; for (unsigned J = 0, N = MD->getNumOperands(); J != N; ++J) StrSet.insert(getMDOperandAsString(MD, J)); } return StrSet; } std::tuple getSPIRVSource(Module *M) { std::tuple Tup; if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::Source).nextOp()) N.get(std::get<0>(Tup)) .get(std::get<1>(Tup)) .setQuiet(true) .get(std::get<2>(Tup)); return Tup; } ConstantInt *mapUInt(Module *M, ConstantInt *I, std::function F) { return ConstantInt::get(I->getType(), F(I->getZExtValue()), false); } ConstantInt *mapSInt(Module *M, ConstantInt *I, std::function F) { return ConstantInt::get(I->getType(), F(I->getSExtValue()), true); } bool isDecoratedSPIRVFunc(const Function *F, std::string *UndecoratedName) { if (!F->hasName() || !F->getName().startswith(kSPIRVName::Prefix)) return false; if (UndecoratedName) *UndecoratedName = undecorateSPIRVFunction(F->getName()); return true; } /// Get TypePrimitiveEnum for special OpenCL type except opencl.block. SPIR::TypePrimitiveEnum getOCLTypePrimitiveEnum(StringRef TyName) { return StringSwitch(TyName) .Case("opencl.image1d_t", SPIR::PRIMITIVE_IMAGE_1D_T) .Case("opencl.image1d_array_t", SPIR::PRIMITIVE_IMAGE_1D_ARRAY_T) .Case("opencl.image1d_buffer_t", SPIR::PRIMITIVE_IMAGE_1D_BUFFER_T) .Case("opencl.image2d_t", SPIR::PRIMITIVE_IMAGE_2D_T) .Case("opencl.image2d_array_t", SPIR::PRIMITIVE_IMAGE_2D_ARRAY_T) .Case("opencl.image3d_t", SPIR::PRIMITIVE_IMAGE_3D_T) .Case("opencl.image2d_msaa_t", SPIR::PRIMITIVE_IMAGE_2D_MSAA_T) .Case("opencl.image2d_array_msaa_t", SPIR::PRIMITIVE_IMAGE_2D_ARRAY_MSAA_T) .Case("opencl.image2d_msaa_depth_t", SPIR::PRIMITIVE_IMAGE_2D_MSAA_DEPTH_T) .Case("opencl.image2d_array_msaa_depth_t", SPIR::PRIMITIVE_IMAGE_2D_ARRAY_MSAA_DEPTH_T) .Case("opencl.image2d_depth_t", SPIR::PRIMITIVE_IMAGE_2D_DEPTH_T) .Case("opencl.image2d_array_depth_t", SPIR::PRIMITIVE_IMAGE_2D_ARRAY_DEPTH_T) .Case("opencl.event_t", SPIR::PRIMITIVE_EVENT_T) .Case("opencl.pipe_t", SPIR::PRIMITIVE_PIPE_T) .Case("opencl.reserve_id_t", SPIR::PRIMITIVE_RESERVE_ID_T) .Case("opencl.queue_t", SPIR::PRIMITIVE_QUEUE_T) .Case("opencl.clk_event_t", SPIR::PRIMITIVE_CLK_EVENT_T) .Case("opencl.sampler_t", SPIR::PRIMITIVE_SAMPLER_T) .Case("struct.ndrange_t", SPIR::PRIMITIVE_NDRANGE_T) .Default( SPIR::PRIMITIVE_NONE); } /// Translates LLVM type to descriptor for mangler. /// \param Signed indicates integer type should be translated as signed. /// \param VoidPtr indicates i8* should be translated as void*. static SPIR::RefParamType transTypeDesc(Type *Ty, const BuiltinArgTypeMangleInfo &Info) { bool Signed = Info.IsSigned; unsigned Attr = Info.Attr; bool VoidPtr = Info.IsVoidPtr; if (Info.IsEnum) return SPIR::RefParamType(new SPIR::PrimitiveType(Info.Enum)); if (Info.IsSampler) return SPIR::RefParamType(new SPIR::PrimitiveType( SPIR::PRIMITIVE_SAMPLER_T)); if (Info.IsAtomic && !Ty->isPointerTy()) { BuiltinArgTypeMangleInfo DTInfo = Info; DTInfo.IsAtomic = false; return SPIR::RefParamType(new SPIR::AtomicType( transTypeDesc(Ty, DTInfo))); } if(auto *IntTy = dyn_cast(Ty)) { switch(IntTy->getBitWidth()) { case 1: return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_BOOL)); case 8: return SPIR::RefParamType(new SPIR::PrimitiveType(Signed? SPIR::PRIMITIVE_CHAR:SPIR::PRIMITIVE_UCHAR)); case 16: return SPIR::RefParamType(new SPIR::PrimitiveType(Signed? SPIR::PRIMITIVE_SHORT:SPIR::PRIMITIVE_USHORT)); case 32: return SPIR::RefParamType(new SPIR::PrimitiveType(Signed? SPIR::PRIMITIVE_INT:SPIR::PRIMITIVE_UINT)); case 64: return SPIR::RefParamType(new SPIR::PrimitiveType(Signed? SPIR::PRIMITIVE_LONG:SPIR::PRIMITIVE_ULONG)); default: llvm_unreachable("invliad int size"); } } if (Ty->isVoidTy()) return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_VOID)); if (Ty->isHalfTy()) return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_HALF)); if (Ty->isFloatTy()) return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_FLOAT)); if (Ty->isDoubleTy()) return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_DOUBLE)); if (Ty->isVectorTy()) { return SPIR::RefParamType(new SPIR::VectorType( transTypeDesc(Ty->getVectorElementType(), Info), Ty->getVectorNumElements())); } if (Ty->isArrayTy()) { return transTypeDesc(PointerType::get(Ty->getArrayElementType(), 0), Info); } if (Ty->isStructTy()) { auto Name = Ty->getStructName(); std::string Tmp; if (Name.startswith(kLLVMTypeName::StructPrefix)) Name = Name.drop_front(strlen(kLLVMTypeName::StructPrefix)); if (Name.startswith(kSPIRVTypeName::PrefixAndDelim)) { Name = Name.substr(sizeof(kSPIRVTypeName::PrefixAndDelim) - 1); Tmp = Name.str(); auto pos = Tmp.find(kSPIRVTypeName::Delimiter); //first dot while (pos != std::string::npos) { Tmp[pos] = '_'; pos = Tmp.find(kSPIRVTypeName::Delimiter, pos); } Name = Tmp = kSPIRVName::Prefix + Tmp; } // ToDo: Create a better unique name for struct without name if (Name.empty()) { std::ostringstream OS; OS << reinterpret_cast(Ty); Name = Tmp = std::string("struct_") + OS.str(); } return SPIR::RefParamType(new SPIR::UserDefinedType(Name)); } if (Ty->isPointerTy()) { auto ET = Ty->getPointerElementType(); SPIR::ParamType *EPT = nullptr; if (auto FT = dyn_cast(ET)) { (void) FT; assert(isVoidFuncTy(FT) && "Not supported"); EPT = new SPIR::BlockType; } else if (auto StructTy = dyn_cast(ET)) { DEBUG(dbgs() << "ptr to struct: " << *Ty << '\n'); auto TyName = StructTy->getStructName(); if (TyName.startswith(kSPR2TypeName::ImagePrefix) || TyName.startswith(kSPR2TypeName::Pipe)) { auto DelimPos = TyName.find_first_of(kSPR2TypeName::Delimiter, strlen(kSPR2TypeName::OCLPrefix)); if (DelimPos != StringRef::npos) TyName = TyName.substr(0, DelimPos); } DEBUG(dbgs() << " type name: " << TyName << '\n'); auto Prim = getOCLTypePrimitiveEnum(TyName); if (StructTy->isOpaque()) { if (TyName == "opencl.block") { auto BlockTy = new SPIR::BlockType; // Handle block with local memory arguments according to OpenCL 2.0 spec. if(Info.IsLocalArgBlock) { SPIR::RefParamType VoidTyRef(new SPIR::PrimitiveType(SPIR::PRIMITIVE_VOID)); auto VoidPtrTy = new SPIR::PointerType(VoidTyRef); VoidPtrTy->setAddressSpace(SPIR::ATTR_LOCAL); // "__local void *" BlockTy->setParam(0, SPIR::RefParamType(VoidPtrTy)); // "..." BlockTy->setParam(1, SPIR::RefParamType( new SPIR::PrimitiveType(SPIR::PRIMITIVE_VAR_ARG))); } EPT = BlockTy; } else if (Prim != SPIR::PRIMITIVE_NONE) { if (Prim == SPIR::PRIMITIVE_PIPE_T) { SPIR::RefParamType OpaqueTyRef(new SPIR::PrimitiveType(Prim)); auto OpaquePtrTy = new SPIR::PointerType(OpaqueTyRef); OpaquePtrTy->setAddressSpace(getOCLOpaqueTypeAddrSpace(Prim)); EPT = OpaquePtrTy; } else { EPT = new SPIR::PrimitiveType(Prim); } } } else if (Prim == SPIR::PRIMITIVE_NDRANGE_T) // ndrange_t is not opaque type EPT = new SPIR::PrimitiveType(SPIR::PRIMITIVE_NDRANGE_T); } if (EPT) return SPIR::RefParamType(EPT); if (VoidPtr && ET->isIntegerTy(8)) ET = Type::getVoidTy(ET->getContext()); auto PT = new SPIR::PointerType(transTypeDesc(ET, Info)); PT->setAddressSpace(static_cast( Ty->getPointerAddressSpace() + (unsigned)SPIR::ATTR_ADDR_SPACE_FIRST)); for (unsigned I = SPIR::ATTR_QUALIFIER_FIRST, E = SPIR::ATTR_QUALIFIER_LAST; I <= E; ++I) PT->setQualifier(static_cast(I), I & Attr); return SPIR::RefParamType(PT); } DEBUG(dbgs() << "[transTypeDesc] " << *Ty << '\n'); assert (0 && "not implemented"); return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_INT)); } Value * getScalarOrArray(Value *V, unsigned Size, Instruction *Pos) { if (!V->getType()->isPointerTy()) return V; assert((isa(V) || isa(V)) && "unexpected value type"); auto GEP = cast(V); assert(GEP->getNumOperands() == 3 && "must be a GEP from an array"); auto P = GEP->getOperand(0); assert(P->getType()->getPointerElementType()->getArrayNumElements() == Size); auto Index0 = GEP->getOperand(1); (void) Index0; assert(dyn_cast(Index0)->getZExtValue() == 0); auto Index1 = GEP->getOperand(2); (void) Index1; assert(dyn_cast(Index1)->getZExtValue() == 0); return new LoadInst(P, "", Pos); } Constant * getScalarOrVectorConstantInt(Type *T, uint64_t V, bool isSigned) { if (auto IT = dyn_cast(T)) return ConstantInt::get(IT, V); if (auto VT = dyn_cast(T)) { std::vector EV(VT->getVectorNumElements(), getScalarOrVectorConstantInt(VT->getVectorElementType(), V, isSigned)); return ConstantVector::get(EV); } llvm_unreachable("Invalid type"); return nullptr; } Value * getScalarOrArrayConstantInt(Instruction *Pos, Type *T, unsigned Len, uint64_t V, bool isSigned) { if (auto IT = dyn_cast(T)) { assert(Len == 1 && "Invalid length"); return ConstantInt::get(IT, V, isSigned); } if (auto PT = dyn_cast(T)) { auto ET = PT->getPointerElementType(); auto AT = ArrayType::get(ET, Len); std::vector EV(Len, ConstantInt::get(ET, V, isSigned)); auto CA = ConstantArray::get(AT, EV); auto Alloca = new AllocaInst(AT, "", Pos); new StoreInst(CA, Alloca, Pos); auto Zero = ConstantInt::getNullValue(Type::getInt32Ty(T->getContext())); Value *Index[] = {Zero, Zero}; auto Ret = GetElementPtrInst::CreateInBounds(Alloca, Index, "", Pos); DEBUG(dbgs() << "[getScalarOrArrayConstantInt] Alloca: " << *Alloca << ", Return: " << *Ret << '\n'); return Ret; } if (auto AT = dyn_cast(T)) { auto ET = AT->getArrayElementType(); assert(AT->getArrayNumElements() == Len); std::vector EV(Len, ConstantInt::get(ET, V, isSigned)); auto Ret = ConstantArray::get(AT, EV); DEBUG(dbgs() << "[getScalarOrArrayConstantInt] Array type: " << *AT << ", Return: " << *Ret << '\n'); return Ret; } llvm_unreachable("Invalid type"); return nullptr; } void dumpUsers(Value* V, StringRef Prompt) { if (!V) return; DEBUG(dbgs() << Prompt << " Users of " << *V << " :\n"); for (auto UI = V->user_begin(), UE = V->user_end(); UI != UE; ++UI) DEBUG(dbgs() << " " << **UI << '\n'); } std::string getSPIRVTypeName(StringRef BaseName, StringRef Postfixes) { assert(!BaseName.empty() && "Invalid SPIR-V type name"); auto TN = std::string(kSPIRVTypeName::PrefixAndDelim) + BaseName.str(); if (Postfixes.empty()) return TN; return TN + kSPIRVTypeName::Delimiter + Postfixes.str(); } bool isSPIRVConstantName(StringRef TyName) { if (TyName == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler) || TyName == getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage)) return true; return false; } Type * getSPIRVTypeByChangeBaseTypeName(Module *M, Type *T, StringRef OldName, StringRef NewName) { StringRef Postfixes; if (isSPIRVType(T, OldName, &Postfixes)) return getOrCreateOpaquePtrType(M, getSPIRVTypeName(NewName, Postfixes)); DEBUG(dbgs() << " Invalid SPIR-V type " << *T << '\n'); llvm_unreachable("Invalid SPIRV-V type"); return nullptr; } std::string getSPIRVImageTypePostfixes(StringRef SampledType, SPIRVTypeImageDescriptor Desc, SPIRVAccessQualifierKind Acc) { std::string S; raw_string_ostream OS(S); OS << SampledType << kSPIRVTypeName::PostfixDelim << Desc.Dim << kSPIRVTypeName::PostfixDelim << Desc.Depth << kSPIRVTypeName::PostfixDelim << Desc.Arrayed << kSPIRVTypeName::PostfixDelim << Desc.MS << kSPIRVTypeName::PostfixDelim << Desc.Sampled << kSPIRVTypeName::PostfixDelim << Desc.Format << kSPIRVTypeName::PostfixDelim << Acc; return OS.str(); } std::string getSPIRVImageSampledTypeName(SPIRVType *Ty) { switch(Ty->getOpCode()) { case OpTypeVoid: return kSPIRVImageSampledTypeName::Void; case OpTypeInt: if (Ty->getIntegerBitWidth() == 32) { if (static_cast(Ty)->isSigned()) return kSPIRVImageSampledTypeName::Int; else return kSPIRVImageSampledTypeName::UInt; } break; case OpTypeFloat: switch(Ty->getFloatBitWidth()) { case 16: return kSPIRVImageSampledTypeName::Half; case 32: return kSPIRVImageSampledTypeName::Float; default: break; } break; default: break; } llvm_unreachable("Invalid sampled type for image"); } //ToDo: Find a way to represent uint sampled type in LLVM, maybe an // opaque type. Type* getLLVMTypeForSPIRVImageSampledTypePostfix(StringRef Postfix, LLVMContext &Ctx) { if (Postfix == kSPIRVImageSampledTypeName::Void) return Type::getVoidTy(Ctx); if (Postfix == kSPIRVImageSampledTypeName::Float) return Type::getFloatTy(Ctx); if (Postfix == kSPIRVImageSampledTypeName::Half) return Type::getHalfTy(Ctx); if (Postfix == kSPIRVImageSampledTypeName::Int || Postfix == kSPIRVImageSampledTypeName::UInt) return Type::getInt32Ty(Ctx); llvm_unreachable("Invalid sampled type postfix"); } std::string mapOCLTypeNameToSPIRV(StringRef Name, StringRef Acc) { std::string BaseTy; std::string Postfixes; raw_string_ostream OS(Postfixes); if (!Acc.empty()) OS << kSPIRVTypeName::PostfixDelim; if (Name.startswith(kSPR2TypeName::Pipe)) { BaseTy = kSPIRVTypeName::Pipe; OS << SPIRSPIRVAccessQualifierMap::map(Acc); } else if (Name.startswith(kSPR2TypeName::ImagePrefix)) { SmallVector SubStrs; const char Delims[] = {kSPR2TypeName::Delimiter, 0}; Name.split(SubStrs, Delims); std::string ImageTyName = SubStrs[1].str(); if (hasAccessQualifiedName(Name)) ImageTyName.erase(ImageTyName.size() - 5, 3); auto Desc = map(ImageTyName); DEBUG(dbgs() << "[trans image type] " << SubStrs[1] << " => " << "(" << (unsigned)Desc.Dim << ", " << Desc.Depth << ", " << Desc.Arrayed << ", " << Desc.MS << ", " << Desc.Sampled << ", " << Desc.Format << ")\n"); BaseTy = kSPIRVTypeName::Image; OS << getSPIRVImageTypePostfixes(kSPIRVImageSampledTypeName::Void, Desc, SPIRSPIRVAccessQualifierMap::map(Acc)); } else { DEBUG(dbgs() << "Mapping of " << Name << " is not implemented\n"); llvm_unreachable("Not implemented"); } return getSPIRVTypeName(BaseTy, OS.str()); } bool eraseIfNoUse(Function *F) { bool changed = false; if (!F) return changed; if (!GlobalValue::isInternalLinkage(F->getLinkage()) && !F->isDeclaration()) return changed; dumpUsers(F, "[eraseIfNoUse] "); for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { auto U = *UI++; if (auto CE = dyn_cast(U)){ if (CE->use_empty()) { CE->dropAllReferences(); changed = true; } } } if (F->use_empty()) { DEBUG(dbgs() << "Erase "; F->printAsOperand(dbgs()); dbgs() << '\n'); F->eraseFromParent(); changed = true; } return changed; } void eraseIfNoUse(Value *V) { if (!V->use_empty()) return; if (Constant *C = dyn_cast(V)) { C->destroyConstant(); return; } if (Instruction *I = dyn_cast(V)) { if (!I->mayHaveSideEffects()) I->eraseFromParent(); } eraseIfNoUse(dyn_cast(V)); } bool eraseUselessFunctions(Module *M) { bool changed = false; for (auto I = M->begin(), E = M->end(); I != E;) changed |= eraseIfNoUse(static_cast(I++)); return changed; } std::string mangleBuiltin(const std::string &UniqName, ArrayRef ArgTypes, BuiltinFuncMangleInfo* BtnInfo) { if (!BtnInfo) return UniqName; BtnInfo->init(UniqName); std::string MangledName; DEBUG(dbgs() << "[mangle] " << UniqName << " => "); SPIR::NameMangler Mangler(SPIR::SPIR20); SPIR::FunctionDescriptor FD; FD.name = BtnInfo->getUnmangledName(); bool BIVarArgNegative = BtnInfo->getVarArg() < 0; if (ArgTypes.empty()) { // Function signature cannot be ()(void, ...) so if there is an ellipsis // it must be ()(...) if(BIVarArgNegative) { FD.parameters.emplace_back(SPIR::RefParamType(new SPIR::PrimitiveType( SPIR::PRIMITIVE_VOID))); } } else { for (unsigned I = 0, E = BIVarArgNegative ? ArgTypes.size() : (unsigned)BtnInfo->getVarArg(); I != E; ++I) { auto T = ArgTypes[I]; FD.parameters.emplace_back(transTypeDesc(T, BtnInfo->getTypeMangleInfo(I))); } } // Ellipsis must be the last argument of any function if(!BIVarArgNegative) { assert((unsigned)BtnInfo->getVarArg() <= ArgTypes.size() && "invalid index of an ellipsis"); FD.parameters.emplace_back(SPIR::RefParamType(new SPIR::PrimitiveType( SPIR::PRIMITIVE_VAR_ARG))); } Mangler.mangle(FD, MangledName); DEBUG(dbgs() << MangledName << '\n'); return MangledName; } /// Check if access qualifier is encoded in the type name. bool hasAccessQualifiedName(StringRef TyName) { if (TyName.endswith("_ro_t") || TyName.endswith("_wo_t") || TyName.endswith("_rw_t")) return true; return false; } /// Get access qualifier from the type name. StringRef getAccessQualifier(StringRef TyName) { assert(hasAccessQualifiedName(TyName) && "Type is not qualified with access."); auto Acc = TyName.substr(TyName.size() - 4, 2); return llvm::StringSwitch(Acc) .Case("ro", "read_only") .Case("wo", "write_only") .Case("rw", "read_write") .Default(""); } /// Translates OpenCL image type names to SPIR-V. Type *getSPIRVImageTypeFromOCL(Module *M, Type *ImageTy) { assert(isOCLImageType(ImageTy) && "Unsupported type"); auto ImageTypeName = ImageTy->getPointerElementType()->getStructName(); std::string Acc = kAccessQualName::ReadOnly; if (hasAccessQualifiedName(ImageTypeName)) Acc = getAccessQualifier(ImageTypeName); return getOrCreateOpaquePtrType(M, mapOCLTypeNameToSPIRV(ImageTypeName, Acc)); } }