| //===- 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 <functional> |
| #include <sstream> |
| |
| #define DEBUG_TYPE "spirv" |
| |
| namespace SPIRV{ |
| |
| #ifdef _SPIRV_SUPPORT_TEXT_FMT |
| cl::opt<bool, true> |
| UseTextFormat("spirv-text", |
| cl::desc("Use text format for SPIR-V for debugging purpose"), |
| cl::location(SPIRVUseTextFormat)); |
| #endif |
| |
| #ifdef _SPIRVDBG |
| cl::opt<bool, true> |
| 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<ConstantExpr>(V); |
| if (Cast && Cast->isCast()) { |
| return removeCast(Cast->getOperand(0)); |
| } |
| if (auto Cast = dyn_cast<CastInst>(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<IntegerType>(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<VectorType>(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<Type*>& 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<PointerType>(Ty)) |
| if (auto ST = dyn_cast<StructType>(PT->getElementType())) |
| if (ST->isOpaque()) |
| return true; |
| return false; |
| } |
| |
| bool |
| isPointerToOpaqueStructType(llvm::Type* Ty, const std::string &Name) { |
| if (auto PT = dyn_cast<PointerType>(Ty)) |
| if (auto ST = dyn_cast<StructType>(PT->getElementType())) |
| if (ST->isOpaque() && ST->getName() == Name) |
| return true; |
| return false; |
| } |
| |
| bool |
| isOCLImageType(llvm::Type* Ty, StringRef *Name) { |
| if (auto PT = dyn_cast<PointerType>(Ty)) |
| if (auto ST = dyn_cast<StructType>(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<PointerType>(Ty)) |
| if (auto ST = dyn_cast<StructType>(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<Type *> 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<Value *> |
| getArguments(CallInst* CI, unsigned Start, unsigned End) { |
| std::vector<Value*> 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<ConstantInt>(CI->getArgOperand(I))->getZExtValue(); |
| } |
| |
| Scope getArgAsScope(CallInst *CI, unsigned I){ |
| return static_cast<Scope>(getArgAsInt(CI, I)); |
| } |
| |
| Decoration getArgAsDecoration(CallInst *CI, unsigned I) { |
| return static_cast<Decoration>(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<StringRef> &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<OCLExtOpKind>(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<SPIRVFPRoundingModeKind>(Postfix.str())); |
| |
| return nullptr; |
| } |
| |
| SPIRVValue * |
| addDecorations(SPIRVValue *Target, const SmallVectorImpl<std::string>& 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<std::string>(static_cast<SPIRVFPRoundingModeKind>(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<std::string> *Dec) { |
| Op OC; |
| SmallVector<StringRef, 2> 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<StringRef, 2> 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<PointerType>(T) && |
| isa<FunctionType>(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<std::string (CallInst *, std::vector<Value *> &)>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<std::string (CallInst *, std::vector<Value *> &, |
| Type *&RetTy)>ArgMutate, |
| std::function<Instruction *(CallInst *)> 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<std::string (CallInst *, std::vector<Value *> &)>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<CallInst>(*I++)) |
| mutateCallInst(M, CI, ArgMutate, Mangle, Attrs, TakeFuncName); |
| } |
| if (F->use_empty()) |
| F->eraseFromParent(); |
| } |
| |
| CallInst * |
| mutateCallInstSPIRV(Module *M, CallInst *CI, |
| std::function<std::string (CallInst *, std::vector<Value *> &)>ArgMutate, |
| AttributeSet *Attrs) { |
| BuiltinFuncMangleInfo BtnInfo; |
| return mutateCallInst(M, CI, ArgMutate, &BtnInfo, Attrs); |
| } |
| |
| Instruction * |
| mutateCallInstSPIRV(Module *M, CallInst *CI, |
| std::function<std::string (CallInst *, std::vector<Value *> &, |
| Type *&RetTy)> ArgMutate, |
| std::function<Instruction *(CallInst *)> RetMutate, |
| AttributeSet *Attrs) { |
| BuiltinFuncMangleInfo BtnInfo; |
| return mutateCallInst(M, CI, ArgMutate, RetMutate, &BtnInfo, Attrs); |
| } |
| |
| CallInst * |
| addCallInst(Module *M, StringRef FuncName, Type *RetTy, ArrayRef<Value *> 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<Value *> 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<Value *> &Ops, |
| ValueVecRange Range) { |
| auto Vec = addVector(InsPos, Range); |
| Ops.erase(Range.first, Range.second); |
| Ops.push_back(Vec); |
| } |
| |
| void |
| expandVector(Instruction *InsPos, std::vector<Value *> &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<PointerType>(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<Value *> getInt32(Module *M, const std::vector<int> &value) { |
| std::vector<Value *> 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<ConstantInt>(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<MDString>(Op)) { |
| return Str->getString().str(); |
| } |
| return ""; |
| } |
| |
| Type* |
| getMDOperandAsType(MDNode* N, unsigned I) { |
| return cast<ValueAsMetadata>(N->getOperand(I))->getType(); |
| } |
| |
| std::set<std::string> |
| getNamedMDAsStringSet(Module *M, const std::string &MDName) { |
| NamedMDNode *NamedMD = M->getNamedMetadata(MDName); |
| std::set<std::string> 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<unsigned, unsigned, std::string> |
| getSPIRVSource(Module *M) { |
| std::tuple<unsigned, unsigned, std::string> 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<unsigned(unsigned)> F) { |
| return ConstantInt::get(I->getType(), F(I->getZExtValue()), false); |
| } |
| |
| ConstantInt *mapSInt(Module *M, ConstantInt *I, |
| std::function<int(int)> 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<SPIR::TypePrimitiveEnum>(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<IntegerType>(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<size_t>(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<FunctionType>(ET)) { |
| (void) FT; |
| assert(isVoidFuncTy(FT) && "Not supported"); |
| EPT = new SPIR::BlockType; |
| } else if (auto StructTy = dyn_cast<StructType>(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<SPIR::TypeAttributeEnum>( |
| 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<SPIR::TypeAttributeEnum>(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<ConstantExpr>(V) || isa<GetElementPtrInst>(V)) && |
| "unexpected value type"); |
| auto GEP = cast<User>(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<ConstantInt>(Index0)->getZExtValue() == 0); |
| auto Index1 = GEP->getOperand(2); |
| (void) Index1; |
| assert(dyn_cast<ConstantInt>(Index1)->getZExtValue() == 0); |
| return new LoadInst(P, "", Pos); |
| } |
| |
| Constant * |
| getScalarOrVectorConstantInt(Type *T, uint64_t V, bool isSigned) { |
| if (auto IT = dyn_cast<IntegerType>(T)) |
| return ConstantInt::get(IT, V); |
| if (auto VT = dyn_cast<VectorType>(T)) { |
| std::vector<Constant *> 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<IntegerType>(T)) { |
| assert(Len == 1 && "Invalid length"); |
| return ConstantInt::get(IT, V, isSigned); |
| } |
| if (auto PT = dyn_cast<PointerType>(T)) { |
| auto ET = PT->getPointerElementType(); |
| auto AT = ArrayType::get(ET, Len); |
| std::vector<Constant *> 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<ArrayType>(T)) { |
| auto ET = AT->getArrayElementType(); |
| assert(AT->getArrayNumElements() == Len); |
| std::vector<Constant *> 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<SPIRVTypeInt *>(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<StringRef, 4> 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<SPIRVTypeImageDescriptor>(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<ConstantExpr>(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<Constant>(V)) { |
| C->destroyConstant(); |
| return; |
| } |
| if (Instruction *I = dyn_cast<Instruction>(V)) { |
| if (!I->mayHaveSideEffects()) |
| I->eraseFromParent(); |
| } |
| eraseIfNoUse(dyn_cast<Function>(V)); |
| } |
| |
| bool |
| eraseUselessFunctions(Module *M) { |
| bool changed = false; |
| for (auto I = M->begin(), E = M->end(); I != E;) |
| changed |= eraseIfNoUse(static_cast<Function*>(I++)); |
| return changed; |
| } |
| |
| std::string |
| mangleBuiltin(const std::string &UniqName, |
| ArrayRef<Type*> 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<StringRef>(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)); |
| } |
| } |