//===- SPIRVReader.cpp - Converts SPIR-V to LLVM ----------------*- C++ -*-===// // // The LLVM/SPIR-V 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 implements conversion of SPIR-V binary to LLVM IR. /// //===----------------------------------------------------------------------===// #include "SPIRVUtil.h" #include "SPIRVType.h" #include "SPIRVValue.h" #include "SPIRVModule.h" #include "SPIRVFunction.h" #include "SPIRVBasicBlock.h" #include "SPIRVInstruction.h" #include "SPIRVExtInst.h" #include "SPIRVInternal.h" #include "SPIRVMDBuilder.h" #include "OCLUtil.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/CommandLine.h" #include #include #include #include #include #include #include #include #include #include #define DEBUG_TYPE "spirv" using namespace std; using namespace llvm; using namespace SPIRV; using namespace OCLUtil; namespace SPIRV{ cl::opt SPIRVEnableStepExpansion("spirv-expand-step", cl::init(true), cl::desc("Enable expansion of OpenCL step and smoothstep function")); cl::opt SPIRVGenKernelArgNameMD("spirv-gen-kernel-arg-name-md", cl::init(false), cl::desc("Enable generating OpenCL kernel argument name " "metadata")); cl::opt SPIRVGenImgTypeAccQualPostfix("spirv-gen-image-type-acc-postfix", cl::init(false), cl::desc("Enable generating access qualifier postfix" " in OpenCL image type names")); // Prefix for placeholder global variable name. const char* kPlaceholderPrefix = "placeholder."; // Save the translated LLVM before validation for debugging purpose. static bool DbgSaveTmpLLVM = true; static const char *DbgTmpLLVMFileName = "_tmp_llvmbil.ll"; typedef std::pair < unsigned, AttributeSet > AttributeWithIndex; static bool isOpenCLKernel(SPIRVFunction *BF) { return BF->getModule()->isEntryPoint(ExecutionModelKernel, BF->getId()); } static void dumpLLVM(Module *M, const std::string &FName) { std::error_code EC; raw_fd_ostream FS(FName, EC, sys::fs::F_None); if (EC) { FS << *M; FS.close(); } } static MDNode* getMDNodeStringIntVec(LLVMContext *Context, const std::string& Str, const std::vector& IntVals) { std::vector ValueVec; ValueVec.push_back(MDString::get(*Context, Str)); for (auto &I:IntVals) ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), I))); return MDNode::get(*Context, ValueVec); } static MDNode* getMDTwoInt(LLVMContext *Context, unsigned Int1, unsigned Int2) { std::vector ValueVec; ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), Int1))); ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), Int2))); return MDNode::get(*Context, ValueVec); } #if 0 // this function is currently unneeded static MDNode* getMDString(LLVMContext *Context, const std::string& Str) { std::vector ValueVec; if (!Str.empty()) ValueVec.push_back(MDString::get(*Context, Str)); return MDNode::get(*Context, ValueVec); } #endif static void addOCLVersionMetadata(LLVMContext *Context, Module *M, const std::string &MDName, unsigned Major, unsigned Minor) { NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName); NamedMD->addOperand(getMDTwoInt(Context, Major, Minor)); } static void addNamedMetadataStringSet(LLVMContext *Context, Module *M, const std::string &MDName, const std::set &StrSet) { NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName); std::vector ValueVec; for (auto &&Str : StrSet) { ValueVec.push_back(MDString::get(*Context, Str)); } NamedMD->addOperand(MDNode::get(*Context, ValueVec)); } static void addOCLKernelArgumentMetadata(LLVMContext *Context, std::vector &KernelMD, const std::string &MDName, SPIRVFunction *BF, std::functionFunc){ std::vector ValueVec; ValueVec.push_back(MDString::get(*Context, MDName)); BF->foreachArgument([&](SPIRVFunctionParameter *Arg) { ValueVec.push_back(Func(Arg)); }); KernelMD.push_back(MDNode::get(*Context, ValueVec)); } class SPIRVToLLVMDbgTran { public: SPIRVToLLVMDbgTran(SPIRVModule *TBM, Module *TM) :BM(TBM), M(TM), SpDbg(BM), Builder(*M){ Enable = BM->hasDebugInfo(); } void createCompileUnit() { if (!Enable) return; auto File = SpDbg.getEntryPointFileStr(ExecutionModelKernel, 0); std::string BaseName; std::string Path; splitFileName(File, BaseName, Path); Builder.createCompileUnit(dwarf::DW_LANG_C99, BaseName, Path, "spirv", false, "", 0, "", DICompileUnit::DebugEmissionKind::LineTablesOnly); } void addDbgInfoVersion() { if (!Enable) return; M->addModuleFlag(Module::Warning, "Dwarf Version", dwarf::DWARF_VERSION); M->addModuleFlag(Module::Warning, "Debug Info Version", DEBUG_METADATA_VERSION); } DIFile* getDIFile(const std::string &FileName){ return getOrInsert(FileMap, FileName, [=](){ std::string BaseName; std::string Path; splitFileName(FileName, BaseName, Path); if (!BaseName.empty()) return Builder.createFile(BaseName, Path); else return Builder.createFile("","");//DIFile(); }); } DISubprogram* getDISubprogram(SPIRVFunction *SF, Function *F){ return getOrInsert(FuncMap, F, [=](){ auto DF = getDIFile(SpDbg.getFunctionFileStr(SF)); auto FN = F->getName(); auto LN = SpDbg.getFunctionLineNo(SF); Metadata *Args[] = {Builder.createUnspecifiedType("")}; return Builder.createFunction(static_cast(DF), FN, FN, DF, LN, Builder.createSubroutineType(Builder.getOrCreateTypeArray(Args)), Function::isInternalLinkage(F->getLinkage()), true, LN); }); } void transDbgInfo(SPIRVValue *SV, Value *V) { if (!Enable || !SV->hasLine()) return; if (auto I = dyn_cast(V)) { assert(SV->isInst() && "Invalid instruction"); auto SI = static_cast(SV); assert(SI->getParent() && SI->getParent()->getParent() && "Invalid instruction"); auto Line = SV->getLine(); I->setDebugLoc(DebugLoc::get(Line->getLine(), Line->getColumn(), getDISubprogram(SI->getParent()->getParent(), I->getParent()->getParent()))); } } void finalize() { if (!Enable) return; Builder.finalize(); } private: SPIRVModule *BM; Module *M; SPIRVDbgInfo SpDbg; DIBuilder Builder; bool Enable; std::unordered_map FileMap; std::unordered_map FuncMap; void splitFileName(const std::string &FileName, std::string &BaseName, std::string &Path) { auto Loc = FileName.find_last_of("/\\"); if (Loc != std::string::npos) { BaseName = FileName.substr(Loc + 1); Path = FileName.substr(0, Loc); } else { BaseName = FileName; Path = "."; } } }; class SPIRVToLLVM { public: SPIRVToLLVM(Module *LLVMModule, SPIRVModule *TheSPIRVModule) :M(LLVMModule), BM(TheSPIRVModule), DbgTran(BM, M){ assert(M); Context = &M->getContext(); } std::string getOCLBuiltinName(SPIRVInstruction* BI); std::string getOCLConvertBuiltinName(SPIRVInstruction *BI); std::string getOCLGenericCastToPtrName(SPIRVInstruction *BI); Type *transType(SPIRVType *BT, bool IsClassMember = false); std::string transTypeToOCLTypeName(SPIRVType *BT, bool IsSigned = true); std::vector transTypeVector(const std::vector&); bool translate(); bool transAddressingModel(); Value *transValue(SPIRVValue *, Function *F, BasicBlock *, bool CreatePlaceHolder = true); Value *transValueWithoutDecoration(SPIRVValue *, Function *F, BasicBlock *, bool CreatePlaceHolder = true); bool transDecoration(SPIRVValue *, Value *); bool transAlign(SPIRVValue *, Value *); Instruction *transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB); std::vector transValue(const std::vector&, Function *F, BasicBlock *); Function *transFunction(SPIRVFunction *F); bool transFPContractMetadata(); bool transKernelMetadata(); bool transNonTemporalMetadata(Instruction *I); bool transSourceLanguage(); bool transSourceExtension(); void transGeneratorMD(); Value *transConvertInst(SPIRVValue* BV, Function* F, BasicBlock* BB); Instruction *transBuiltinFromInst(const std::string& FuncName, SPIRVInstruction* BI, BasicBlock* BB); Instruction *transOCLBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB); Instruction *transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB); Instruction *transOCLBarrierFence(SPIRVInstruction* BI, BasicBlock *BB); void transOCLVectorLoadStore(std::string& UnmangledName, std::vector &BArgs); /// Post-process translated LLVM module for OpenCL. bool postProcessOCL(); /// \brief Post-process OpenCL builtin functions returning struct type. /// /// Some OpenCL builtin functions are translated to SPIR-V instructions with /// struct type result, e.g. NDRange creation functions. Such functions /// need to be post-processed to return the struct through sret argument. bool postProcessOCLBuiltinReturnStruct(Function *F); /// \brief Post-process OpenCL builtin functions having block argument. /// /// These functions are translated to functions with function pointer type /// argument first, then post-processed to have block argument. bool postProcessOCLBuiltinWithFuncPointer(Function *F, Function::arg_iterator I); /// \brief Post-process OpenCL builtin functions having array argument. /// /// These functions are translated to functions with array type argument /// first, then post-processed to have pointer arguments. bool postProcessOCLBuiltinWithArrayArguments(Function *F, const std::string &DemangledName); /// \brief Post-process OpImageSampleExplicitLod. /// sampled_image = __spirv_SampledImage__(image, sampler); /// return __spirv_ImageSampleExplicitLod__(sampled_image, image_operands, /// ...); /// => /// read_image(image, sampler, ...) /// \return transformed call instruction. Instruction *postProcessOCLReadImage(SPIRVInstruction *BI, CallInst *CI, const std::string &DemangledName); /// \brief Post-process OpImageWrite. /// return write_image(image, coord, color, image_operands, ...); /// => /// write_image(image, coord, ..., color) /// \return transformed call instruction. CallInst *postProcessOCLWriteImage(SPIRVInstruction *BI, CallInst *CI, const std::string &DemangledName); /// \brief Post-process OpBuildNDRange. /// OpBuildNDRange GlobalWorkSize, LocalWorkSize, GlobalWorkOffset /// => /// call ndrange_XD(GlobalWorkOffset, GlobalWorkSize, LocalWorkSize) /// \return transformed call instruction. CallInst *postProcessOCLBuildNDRange(SPIRVInstruction *BI, CallInst *CI, const std::string &DemangledName); /// \brief Expand OCL builtin functions with scalar argument, e.g. /// step, smoothstep. /// gentype func (fp edge, gentype x) /// => /// gentype func (gentype edge, gentype x) /// \return transformed call instruction. CallInst *expandOCLBuiltinWithScalarArg(CallInst* CI, const std::string &FuncName); /// \brief Post-process OpGroupAll and OpGroupAny instructions translation. /// i1 func ( arg) /// => /// i32 func ( arg) /// \return transformed call instruction. Instruction *postProcessGroupAllAny(CallInst *CI, const std::string &DemangledName); typedef DenseMap SPIRVToLLVMTypeMap; typedef DenseMap SPIRVToLLVMValueMap; typedef DenseMap SPIRVToLLVMFunctionMap; typedef DenseMap BuiltinVarMap; // A SPIRV value may be translated to a load instruction of a placeholder // global variable. This map records load instruction of these placeholders // which are supposed to be replaced by the real values later. typedef std::map SPIRVToLLVMPlaceholderMap; private: Module *M; BuiltinVarMap BuiltinGVMap; LLVMContext *Context; SPIRVModule *BM; SPIRVToLLVMTypeMap TypeMap; SPIRVToLLVMValueMap ValueMap; SPIRVToLLVMFunctionMap FuncMap; SPIRVToLLVMPlaceholderMap PlaceholderMap; SPIRVToLLVMDbgTran DbgTran; Type *mapType(SPIRVType *BT, Type *T) { SPIRVDBG(dbgs() << *T << '\n';) TypeMap[BT] = T; return T; } // If a value is mapped twice, the existing mapped value is a placeholder, // which must be a load instruction of a global variable whose name starts // with kPlaceholderPrefix. Value *mapValue(SPIRVValue *BV, Value *V) { auto Loc = ValueMap.find(BV); if (Loc != ValueMap.end()) { if (Loc->second == V) return V; auto LD = dyn_cast(Loc->second); auto Placeholder = dyn_cast(LD->getPointerOperand()); assert (LD && Placeholder && Placeholder->getName().startswith(kPlaceholderPrefix) && "A value is translated twice"); // Replaces placeholders for PHI nodes LD->replaceAllUsesWith(V); LD->dropAllReferences(); LD->removeFromParent(); Placeholder->dropAllReferences(); Placeholder->removeFromParent(); } ValueMap[BV] = V; return V; } bool isSPIRVBuiltinVariable(GlobalVariable *GV, SPIRVBuiltinVariableKind *Kind = nullptr) { auto Loc = BuiltinGVMap.find(GV); if (Loc == BuiltinGVMap.end()) return false; if (Kind) *Kind = Loc->second; return true; } // OpenCL function always has NoUnwound attribute. // Change this if it is no longer true. bool isFuncNoUnwind() const { return true;} bool isSPIRVCmpInstTransToLLVMInst(SPIRVInstruction *BI) const; bool transOCLBuiltinsFromVariables(); bool transOCLBuiltinFromVariable(GlobalVariable *GV, SPIRVBuiltinVariableKind Kind); MDString *transOCLKernelArgTypeName(SPIRVFunctionParameter *); Value *mapFunction(SPIRVFunction *BF, Function *F) { SPIRVDBG(spvdbgs() << "[mapFunction] " << *BF << " -> "; dbgs() << *F << '\n';) FuncMap[BF] = F; return F; } Value *getTranslatedValue(SPIRVValue *BV); Type *getTranslatedType(SPIRVType *BT); SPIRVErrorLog &getErrorLog() { return BM->getErrorLog(); } void setCallingConv(CallInst *Call) { Function *F = Call->getCalledFunction(); assert(F); Call->setCallingConv(F->getCallingConv()); } void setAttrByCalledFunc(CallInst *Call); Type *transFPType(SPIRVType* T); BinaryOperator *transShiftLogicalBitwiseInst(SPIRVValue* BV, BasicBlock* BB, Function* F); void transFlags(llvm::Value* V); Instruction *transCmpInst(SPIRVValue* BV, BasicBlock* BB, Function* F); void transOCLBuiltinFromInstPreproc(SPIRVInstruction* BI, Type *&RetTy, std::vector &Args); Instruction* transOCLBuiltinPostproc(SPIRVInstruction* BI, CallInst* CI, BasicBlock* BB, const std::string &DemangledName); std::string transOCLImageTypeName(SPIRV::SPIRVTypeImage* ST); std::string transOCLSampledImageTypeName(SPIRV::SPIRVTypeSampledImage* ST); std::string transOCLPipeTypeName(SPIRV::SPIRVTypePipe* ST, bool UseSPIRVFriendlyFormat = false, int PipeAccess = 0); std::string transOCLPipeStorageTypeName(SPIRV::SPIRVTypePipeStorage* PST); std::string transOCLImageTypeAccessQualifier(SPIRV::SPIRVTypeImage* ST); std::string transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST); Value *oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS); Value * oclTransConstantPipeStorage(SPIRV::SPIRVConstantPipeStorage* BCPS); void setName(llvm::Value* V, SPIRVValue* BV); void insertImageNameAccessQualifier(SPIRV::SPIRVTypeImage* ST, std::string &Name); template bool foreachFuncCtlMask(Source, Func); llvm::GlobalValue::LinkageTypes transLinkageType(const SPIRVValue* V); Instruction *transOCLAllAny(SPIRVInstruction* BI, BasicBlock *BB); Instruction *transOCLRelational(SPIRVInstruction* BI, BasicBlock *BB); CallInst *transOCLBarrier(BasicBlock *BB, SPIRVWord ExecScope, SPIRVWord MemSema, SPIRVWord MemScope); CallInst *transOCLMemFence(BasicBlock *BB, SPIRVWord MemSema, SPIRVWord MemScope); }; Type * SPIRVToLLVM::getTranslatedType(SPIRVType *BV){ auto Loc = TypeMap.find(BV); if (Loc != TypeMap.end()) return Loc->second; return nullptr; } Value * SPIRVToLLVM::getTranslatedValue(SPIRVValue *BV){ auto Loc = ValueMap.find(BV); if (Loc != ValueMap.end()) return Loc->second; return nullptr; } void SPIRVToLLVM::setAttrByCalledFunc(CallInst *Call) { Function *F = Call->getCalledFunction(); assert(F); if (F->isIntrinsic()) { return; } Call->setCallingConv(F->getCallingConv()); Call->setAttributes(F->getAttributes()); } bool SPIRVToLLVM::transOCLBuiltinsFromVariables(){ std::vector WorkList; for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) { SPIRVBuiltinVariableKind Kind; auto I1 = static_cast(I); if (!isSPIRVBuiltinVariable(I1, &Kind)) continue; if (!transOCLBuiltinFromVariable(I1, Kind)) return false; WorkList.push_back(I1); } for (auto &I:WorkList) { I->dropAllReferences(); I->removeFromParent(); } return true; } // For integer types shorter than 32 bit, unsigned/signedness can be inferred // from zext/sext attribute. MDString * SPIRVToLLVM::transOCLKernelArgTypeName(SPIRVFunctionParameter *Arg) { auto Ty = Arg->isByVal() ? Arg->getType()->getPointerElementType() : Arg->getType(); return MDString::get(*Context, transTypeToOCLTypeName(Ty, !Arg->isZext())); } // Variable like GlobalInvolcationId[x] -> get_global_id(x). // Variable like WorkDim -> get_work_dim(). bool SPIRVToLLVM::transOCLBuiltinFromVariable(GlobalVariable *GV, SPIRVBuiltinVariableKind Kind) { std::string FuncName = SPIRSPIRVBuiltinVariableMap::rmap(Kind); std::string MangledName; Type *ReturnTy = GV->getType()->getPointerElementType(); bool IsVec = ReturnTy->isVectorTy(); if (IsVec) ReturnTy = cast(ReturnTy)->getElementType(); std::vector ArgTy; if (IsVec) ArgTy.push_back(Type::getInt32Ty(*Context)); MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); Function *Func = M->getFunction(MangledName); if (!Func) { FunctionType *FT = FunctionType::get(ReturnTy, ArgTy, false); Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); Func->setCallingConv(CallingConv::SPIR_FUNC); Func->addFnAttr(Attribute::NoUnwind); Func->addFnAttr(Attribute::ReadNone); } std::vector Deletes; std::vector Uses; for (auto UI = GV->user_begin(), UE = GV->user_end(); UI != UE; ++UI) { assert (isa(*UI) && "Unsupported use"); auto LD = dyn_cast(*UI); if (!IsVec) { Uses.push_back(LD); Deletes.push_back(LD); continue; } for (auto LDUI = LD->user_begin(), LDUE = LD->user_end(); LDUI != LDUE; ++LDUI) { assert(isa(*LDUI) && "Unsupported use"); auto EEI = dyn_cast(*LDUI); Uses.push_back(EEI); Deletes.push_back(EEI); } Deletes.push_back(LD); } for (auto &I:Uses) { std::vector Arg; if (auto EEI = dyn_cast(I)) Arg.push_back(EEI->getIndexOperand()); auto Call = CallInst::Create(Func, Arg, "", I); Call->takeName(I); setAttrByCalledFunc(Call); SPIRVDBG(dbgs() << "[transOCLBuiltinFromVariable] " << *I << " -> " << *Call << '\n';) I->replaceAllUsesWith(Call); } for (auto &I:Deletes) { I->dropAllReferences(); I->removeFromParent(); } return true; } Type * SPIRVToLLVM::transFPType(SPIRVType* T) { switch(T->getFloatBitWidth()) { case 16: return Type::getHalfTy(*Context); case 32: return Type::getFloatTy(*Context); case 64: return Type::getDoubleTy(*Context); default: llvm_unreachable("Invalid type"); return nullptr; } } std::string SPIRVToLLVM::transOCLImageTypeName(SPIRV::SPIRVTypeImage* ST) { std::string Name = std::string(kSPR2TypeName::OCLPrefix) + rmap(ST->getDescriptor()); if (SPIRVGenImgTypeAccQualPostfix) SPIRVToLLVM::insertImageNameAccessQualifier(ST, Name); return Name; } std::string SPIRVToLLVM::transOCLSampledImageTypeName(SPIRV::SPIRVTypeSampledImage* ST) { return getSPIRVTypeName(kSPIRVTypeName::SampledImg, getSPIRVImageTypePostfixes(getSPIRVImageSampledTypeName( ST->getImageType()->getSampledType()), ST->getImageType()->getDescriptor(), ST->getImageType()->getAccessQualifier())); } std::string SPIRVToLLVM::transOCLPipeTypeName(SPIRV::SPIRVTypePipe* PT, bool UseSPIRVFriendlyFormat, int PipeAccess){ if (!UseSPIRVFriendlyFormat) return kSPR2TypeName::Pipe; else return std::string(kSPIRVTypeName::PrefixAndDelim) + kSPIRVTypeName::Pipe + kSPIRVTypeName::Delimiter + kSPIRVTypeName::PostfixDelim + PipeAccess; } std::string SPIRVToLLVM::transOCLPipeStorageTypeName(SPIRV::SPIRVTypePipeStorage* PST) { return std::string(kSPIRVTypeName::PrefixAndDelim) + kSPIRVTypeName::PipeStorage; } Type * SPIRVToLLVM::transType(SPIRVType *T, bool IsClassMember) { auto Loc = TypeMap.find(T); if (Loc != TypeMap.end()) return Loc->second; SPIRVDBG(spvdbgs() << "[transType] " << *T << " -> ";) T->validate(); switch(T->getOpCode()) { case OpTypeVoid: return mapType(T, Type::getVoidTy(*Context)); case OpTypeBool: return mapType(T, Type::getInt1Ty(*Context)); case OpTypeInt: return mapType(T, Type::getIntNTy(*Context, T->getIntegerBitWidth())); case OpTypeFloat: return mapType(T, transFPType(T)); case OpTypeArray: return mapType(T, ArrayType::get(transType(T->getArrayElementType()), T->getArrayLength())); case OpTypePointer: return mapType(T, PointerType::get(transType( T->getPointerElementType(), IsClassMember), SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass()))); case OpTypeVector: return mapType(T, VectorType::get(transType(T->getVectorComponentType()), T->getVectorComponentCount())); case OpTypeOpaque: return mapType(T, StructType::create(*Context, T->getName())); case OpTypeFunction: { auto FT = static_cast(T); auto RT = transType(FT->getReturnType()); std::vector PT; for (size_t I = 0, E = FT->getNumParameters(); I != E; ++I) PT.push_back(transType(FT->getParameterType(I))); return mapType(T, FunctionType::get(RT, PT, false)); } case OpTypeImage: { auto ST = static_cast(T); if (ST->isOCLImage()) return mapType(T, getOrCreateOpaquePtrType(M, transOCLImageTypeName(ST))); else llvm_unreachable("Unsupported image type"); return nullptr; } case OpTypeSampler: return mapType(T, Type::getInt32Ty(*Context)); case OpTypeSampledImage: { auto ST = static_cast(T); return mapType(T, getOrCreateOpaquePtrType(M, transOCLSampledImageTypeName(ST))); } case OpTypeStruct: { auto ST = static_cast(T); auto Name = ST->getName(); if (!Name.empty()) { if (auto OldST = M->getTypeByName(Name)) OldST->setName(""); } auto *StructTy = StructType::create(*Context, Name); mapType(ST, StructTy); SmallVector MT; for (size_t I = 0, E = ST->getMemberCount(); I != E; ++I) MT.push_back(transType(ST->getMemberType(I), true)); StructTy->setBody(MT, ST->isPacked()); return StructTy; } case OpTypePipe: { auto PT = static_cast(T); return mapType(T, getOrCreateOpaquePtrType(M, transOCLPipeTypeName(PT, IsClassMember, PT->getAccessQualifier()), getOCLOpaqueTypeAddrSpace(T->getOpCode()))); } case OpTypePipeStorage: { auto PST = static_cast(T); return mapType(T, getOrCreateOpaquePtrType(M, transOCLPipeStorageTypeName(PST), getOCLOpaqueTypeAddrSpace(T->getOpCode()))); } default: { auto OC = T->getOpCode(); if (isOpaqueGenericTypeOpCode(OC)) return mapType(T, getOrCreateOpaquePtrType(M, OCLOpaqueTypeOpCodeMap::rmap(OC), getOCLOpaqueTypeAddrSpace(OC))); llvm_unreachable("Not implemented"); } } return 0; } std::string SPIRVToLLVM::transTypeToOCLTypeName(SPIRVType *T, bool IsSigned) { switch(T->getOpCode()) { case OpTypeVoid: return "void"; case OpTypeBool: return "bool"; case OpTypeInt: { std::string Prefix = IsSigned ? "" : "u"; switch(T->getIntegerBitWidth()) { case 8: return Prefix + "char"; case 16: return Prefix + "short"; case 32: return Prefix + "int"; case 64: return Prefix + "long"; default: llvm_unreachable("invalid integer size"); return Prefix + std::string("int") + T->getIntegerBitWidth() + "_t"; } } break; case OpTypeFloat: switch(T->getFloatBitWidth()){ case 16: return "half"; case 32: return "float"; case 64: return "double"; default: llvm_unreachable("invalid floating pointer bitwidth"); return std::string("float") + T->getFloatBitWidth() + "_t"; } break; case OpTypeArray: return "array"; case OpTypePointer: return transTypeToOCLTypeName(T->getPointerElementType()) + "*"; case OpTypeVector: return transTypeToOCLTypeName(T->getVectorComponentType()) + T->getVectorComponentCount(); case OpTypeOpaque: return T->getName(); case OpTypeFunction: llvm_unreachable("Unsupported"); return "function"; case OpTypeStruct: { auto Name = T->getName(); if (Name.find("struct.") == 0) Name[6] = ' '; else if (Name.find("union.") == 0) Name[5] = ' '; return Name; } case OpTypePipe: return "pipe"; case OpTypeSampler: return "sampler_t"; case OpTypeImage: { std::string Name; Name = rmap(static_cast(T)->getDescriptor()); if (SPIRVGenImgTypeAccQualPostfix) { auto ST = static_cast(T); insertImageNameAccessQualifier(ST, Name); } return Name; } default: if (isOpaqueGenericTypeOpCode(T->getOpCode())) { return OCLOpaqueTypeOpCodeMap::rmap(T->getOpCode()); } llvm_unreachable("Not implemented"); return "unknown"; } } std::vector SPIRVToLLVM::transTypeVector(const std::vector &BT) { std::vector T; for (auto I: BT) T.push_back(transType(I)); return T; } std::vector SPIRVToLLVM::transValue(const std::vector &BV, Function *F, BasicBlock *BB) { std::vector V; for (auto I: BV) V.push_back(transValue(I, F, BB)); return V; } bool SPIRVToLLVM::isSPIRVCmpInstTransToLLVMInst(SPIRVInstruction* BI) const { auto OC = BI->getOpCode(); return isCmpOpCode(OC) && !(OC >= OpLessOrGreater && OC <= OpUnordered); } void SPIRVToLLVM::transFlags(llvm::Value* V) { if(!isa(V)) return; auto OC = cast(V)->getOpcode(); if (OC == Instruction::AShr || OC == Instruction::LShr) { cast(V)->setIsExact(); return; } } void SPIRVToLLVM::setName(llvm::Value* V, SPIRVValue* BV) { auto Name = BV->getName(); if (!Name.empty() && (!V->hasName() || Name != V->getName())) V->setName(Name); } void SPIRVToLLVM::insertImageNameAccessQualifier(SPIRV::SPIRVTypeImage* ST, std::string &Name) { std::string QName = rmap(ST->getAccessQualifier()); // transform: read_only -> ro, write_only -> wo, read_write -> rw QName = QName.substr(0,1) + QName.substr(QName.find("_") + 1, 1) + "_"; assert(!Name.empty() && "image name should not be empty"); Name.insert(Name.size() - 1, QName); } Value * SPIRVToLLVM::transValue(SPIRVValue *BV, Function *F, BasicBlock *BB, bool CreatePlaceHolder){ SPIRVToLLVMValueMap::iterator Loc = ValueMap.find(BV); if (Loc != ValueMap.end() && (!PlaceholderMap.count(BV) || CreatePlaceHolder)) return Loc->second; SPIRVDBG(spvdbgs() << "[transValue] " << *BV << " -> ";) BV->validate(); auto V = transValueWithoutDecoration(BV, F, BB, CreatePlaceHolder); if (!V) { SPIRVDBG(dbgs() << " Warning ! nullptr\n";) return nullptr; } setName(V, BV); if (!transDecoration(BV, V)) { assert (0 && "trans decoration fail"); return nullptr; } transFlags(V); SPIRVDBG(dbgs() << *V << '\n';) return V; } Value * SPIRVToLLVM::transConvertInst(SPIRVValue* BV, Function* F, BasicBlock* BB) { SPIRVUnary* BC = static_cast(BV); auto Src = transValue(BC->getOperand(0), F, BB, BB ? true : false); auto Dst = transType(BC->getType()); CastInst::CastOps CO = Instruction::BitCast; bool IsExt = Dst->getScalarSizeInBits() > Src->getType()->getScalarSizeInBits(); switch (BC->getOpCode()) { case OpPtrCastToGeneric: case OpGenericCastToPtr: CO = Instruction::AddrSpaceCast; break; case OpSConvert: CO = IsExt ? Instruction::SExt : Instruction::Trunc; break; case OpUConvert: CO = IsExt ? Instruction::ZExt : Instruction::Trunc; break; case OpFConvert: CO = IsExt ? Instruction::FPExt : Instruction::FPTrunc; break; default: CO = static_cast(OpCodeMap::rmap(BC->getOpCode())); } assert(CastInst::isCast(CO) && "Invalid cast op code"); SPIRVDBG(if (!CastInst::castIsValid(CO, Src, Dst)) { spvdbgs() << "Invalid cast: " << *BV << " -> "; dbgs() << "Op = " << CO << ", Src = " << *Src << " Dst = " << *Dst << '\n'; }) if (BB) return CastInst::Create(CO, Src, Dst, BV->getName(), BB); return ConstantExpr::getCast(CO, dyn_cast(Src), Dst); } BinaryOperator *SPIRVToLLVM::transShiftLogicalBitwiseInst(SPIRVValue* BV, BasicBlock* BB,Function* F) { SPIRVBinary* BBN = static_cast(BV); assert(BB && "Invalid BB"); Instruction::BinaryOps BO; auto OP = BBN->getOpCode(); if (isLogicalOpCode(OP)) OP = IntBoolOpMap::rmap(OP); BO = static_cast(OpCodeMap::rmap(OP)); auto Inst = BinaryOperator::Create(BO, transValue(BBN->getOperand(0), F, BB), transValue(BBN->getOperand(1), F, BB), BV->getName(), BB); return Inst; } Instruction * SPIRVToLLVM::transCmpInst(SPIRVValue* BV, BasicBlock* BB, Function* F) { SPIRVCompare* BC = static_cast(BV); assert(BB && "Invalid BB"); SPIRVType* BT = BC->getOperand(0)->getType(); Instruction* Inst = nullptr; auto OP = BC->getOpCode(); if (isLogicalOpCode(OP)) OP = IntBoolOpMap::rmap(OP); if (BT->isTypeVectorOrScalarInt() || BT->isTypeVectorOrScalarBool() || BT->isTypePointer()) Inst = new ICmpInst(*BB, CmpMap::rmap(OP), transValue(BC->getOperand(0), F, BB), transValue(BC->getOperand(1), F, BB)); else if (BT->isTypeVectorOrScalarFloat()) Inst = new FCmpInst(*BB, CmpMap::rmap(OP), transValue(BC->getOperand(0), F, BB), transValue(BC->getOperand(1), F, BB)); assert(Inst && "not implemented"); return Inst; } bool SPIRVToLLVM::postProcessOCL() { std::string DemangledName; SPIRVWord SrcLangVer = 0; BM->getSourceLanguage(&SrcLangVer); bool isCPP = SrcLangVer == kOCLVer::CL21; for (auto I = M->begin(), E = M->end(); I != E;) { auto F = I++; if (F->hasName() && F->isDeclaration()) { DEBUG(dbgs() << "[postProcessOCL sret] " << *F << '\n'); if (F->getReturnType()->isStructTy() && oclIsBuiltin(F->getName(), &DemangledName, isCPP)) { if (!postProcessOCLBuiltinReturnStruct(static_cast(F))) return false; } } } for (auto I = M->begin(), E = M->end(); I != E;) { auto F = static_cast(I++); if (F->hasName() && F->isDeclaration()) { DEBUG(dbgs() << "[postProcessOCL func ptr] " << *F << '\n'); auto AI = F->arg_begin(); if (hasFunctionPointerArg(F, AI) && isDecoratedSPIRVFunc(F)) if (!postProcessOCLBuiltinWithFuncPointer(F, AI)) return false; } } for (auto I = M->begin(), E = M->end(); I != E;) { auto F = static_cast(I++); if (F->hasName() && F->isDeclaration()) { DEBUG(dbgs() << "[postProcessOCL array arg] " << *F << '\n'); if (hasArrayArg(F) && oclIsBuiltin(F->getName(), &DemangledName, isCPP)) if (!postProcessOCLBuiltinWithArrayArguments(F, DemangledName)) return false; } } return true; } bool SPIRVToLLVM::postProcessOCLBuiltinReturnStruct(Function *F) { std::string Name = F->getName(); F->setName(Name + ".old"); for (auto I = F->user_begin(), E = F->user_end(); I != E;) { if (auto CI = dyn_cast(*I++)) { auto ST = dyn_cast(*(CI->user_begin())); assert(ST); std::vector ArgTys; getFunctionTypeParameterTypes(F->getFunctionType(), ArgTys); ArgTys.insert(ArgTys.begin(), PointerType::get(F->getReturnType(), SPIRAS_Private)); auto newF = getOrCreateFunction(M, Type::getVoidTy(*Context), ArgTys, Name); newF->setCallingConv(F->getCallingConv()); auto Args = getArguments(CI); Args.insert(Args.begin(), ST->getPointerOperand()); auto NewCI = CallInst::Create(newF, Args, CI->getName(), CI); NewCI->setCallingConv(CI->getCallingConv()); ST->dropAllReferences(); ST->removeFromParent(); CI->dropAllReferences(); CI->removeFromParent(); } } F->dropAllReferences(); F->removeFromParent(); return true; } bool SPIRVToLLVM::postProcessOCLBuiltinWithFuncPointer(Function* F, Function::arg_iterator I) { auto Name = undecorateSPIRVFunction(F->getName()); std::set InvokeFuncPtrs; mutateFunctionOCL (F, [=, &InvokeFuncPtrs]( CallInst *CI, std::vector &Args) { auto ALoc = std::find_if(Args.begin(), Args.end(), [](Value * elem) { return isFunctionPointerType(elem->getType()); }); assert(ALoc != Args.end() && "Buit-in must accept a pointer to function"); assert(isa(*ALoc) && "Invalid function pointer usage"); Value *Ctx = ALoc[1]; Value *CtxLen = ALoc[2]; Value *CtxAlign = ALoc[3]; if (Name == kOCLBuiltinName::EnqueueKernel) assert(Args.end() - ALoc > 3); else assert(Args.end() - ALoc > 0); // Erase arguments what are hanled by "spir_block_bind" according to SPIR 2.0 Args.erase(ALoc + 1, ALoc + 4); InvokeFuncPtrs.insert(*ALoc); // There will be as many calls to spir_block_bind as how much device execution // bult-ins using this block. This doesn't contradict SPIR 2.0 specification. *ALoc = addBlockBind(M, cast(removeCast(*ALoc)), Ctx, CtxLen, CtxAlign, CI); return Name; }); for (auto &I:InvokeFuncPtrs) eraseIfNoUse(I); return true; } bool SPIRVToLLVM::postProcessOCLBuiltinWithArrayArguments(Function* F, const std::string &DemangledName) { DEBUG(dbgs() << "[postProcessOCLBuiltinWithArrayArguments] " << *F << '\n'); auto Attrs = F->getAttributes(); auto Name = F->getName(); mutateFunction(F, [=](CallInst *CI, std::vector &Args) { auto FBegin = CI->getParent()->getParent()->begin()->getFirstInsertionPt(); for (auto &I:Args) { auto T = I->getType(); if (!T->isArrayTy()) continue; auto Alloca = new AllocaInst(T, "", static_cast(FBegin)); new StoreInst(I, Alloca, false, CI); auto Zero = ConstantInt::getNullValue(Type::getInt32Ty(T->getContext())); Value *Index[] = {Zero, Zero}; I = GetElementPtrInst::CreateInBounds(Alloca, Index, "", CI); } return Name; }, nullptr, &Attrs); return true; } // ToDo: Handle unsigned integer return type. May need spec change. Instruction * SPIRVToLLVM::postProcessOCLReadImage(SPIRVInstruction *BI, CallInst* CI, const std::string &FuncName) { AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); StringRef ImageTypeName; bool isDepthImage = false; if (isOCLImageType( (cast(CI->getOperand(0)))->getArgOperand(0)->getType(), &ImageTypeName)) isDepthImage = ImageTypeName.endswith("depth_t"); return mutateCallInstOCL( M, CI, [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { CallInst *CallSampledImg = cast(Args[0]); auto Img = CallSampledImg->getArgOperand(0); assert(isOCLImageType(Img->getType())); auto Sampler = CallSampledImg->getArgOperand(1); Args[0] = Img; Args.insert(Args.begin() + 1, Sampler); if(Args.size() > 4 ) { ConstantInt* ImOp = dyn_cast(Args[3]); ConstantFP* LodVal = dyn_cast(Args[4]); // Drop "Image Operands" argument. Args.erase(Args.begin() + 3, Args.begin() + 4); // If the image operand is LOD and its value is zero, drop it too. if (ImOp && LodVal && LodVal->isNullValue() && ImOp->getZExtValue() == ImageOperandsMask::ImageOperandsLodMask ) Args.erase(Args.begin() + 3, Args.end()); } if (CallSampledImg->hasOneUse()) { CallSampledImg->replaceAllUsesWith( UndefValue::get(CallSampledImg->getType())); CallSampledImg->dropAllReferences(); CallSampledImg->eraseFromParent(); } Type *T = CI->getType(); if (auto VT = dyn_cast(T)) T = VT->getElementType(); RetTy = isDepthImage ? T : CI->getType(); return std::string(kOCLBuiltinName::SampledReadImage) + (T->isFloatingPointTy() ? 'f' : 'i'); }, [=](CallInst *NewCI) -> Instruction * { if (isDepthImage) return InsertElementInst::Create( UndefValue::get(VectorType::get(NewCI->getType(), 4)), NewCI, getSizet(M, 0), "", NewCI->getParent()); return NewCI; }, &Attrs); } CallInst* SPIRVToLLVM::postProcessOCLWriteImage(SPIRVInstruction *BI, CallInst *CI, const std::string &DemangledName) { AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); return mutateCallInstOCL(M, CI, [=](CallInst *, std::vector &Args) { llvm::Type *T = Args[2]->getType(); if (Args.size() > 4) { ConstantInt* ImOp = dyn_cast(Args[3]); ConstantFP* LodVal = dyn_cast(Args[4]); // Drop "Image Operands" argument. Args.erase(Args.begin() + 3, Args.begin() + 4); // If the image operand is LOD and its value is zero, drop it too. if (ImOp && LodVal && LodVal->isNullValue() && ImOp->getZExtValue() == ImageOperandsMask::ImageOperandsLodMask ) Args.erase(Args.begin() + 3, Args.end()); else std::swap(Args[2], Args[3]); } return std::string(kOCLBuiltinName::WriteImage) + (T->isFPOrFPVectorTy() ? 'f' : 'i'); }, &Attrs); } CallInst * SPIRVToLLVM::postProcessOCLBuildNDRange(SPIRVInstruction *BI, CallInst *CI, const std::string &FuncName) { assert(CI->getNumArgOperands() == 3); auto GWS = CI->getArgOperand(0); auto LWS = CI->getArgOperand(1); auto GWO = CI->getArgOperand(2); CI->setArgOperand(0, GWO); CI->setArgOperand(1, GWS); CI->setArgOperand(2, LWS); return CI; } Instruction * SPIRVToLLVM::postProcessGroupAllAny(CallInst *CI, const std::string &DemangledName) { AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); return mutateCallInstSPIRV( M, CI, [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { Type *Int32Ty = Type::getInt32Ty(*Context); RetTy = Int32Ty; Args[1] = CastInst::CreateZExtOrBitCast(Args[1], Int32Ty, "", CI); return DemangledName; }, [=](CallInst *NewCI) -> Instruction * { Type *RetTy = Type::getInt1Ty(*Context); return CastInst::CreateTruncOrBitCast(NewCI, RetTy, "", NewCI->getNextNode()); }, &Attrs); } CallInst * SPIRVToLLVM::expandOCLBuiltinWithScalarArg(CallInst* CI, const std::string &FuncName) { AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); if (!CI->getOperand(0)->getType()->isVectorTy() && CI->getOperand(1)->getType()->isVectorTy()) { return mutateCallInstOCL(M, CI, [=](CallInst *, std::vector &Args){ unsigned vecSize = CI->getOperand(1)->getType()->getVectorNumElements(); Value *NewVec = nullptr; if (auto CA = dyn_cast(Args[0])) NewVec = ConstantVector::getSplat(vecSize, CA); else { NewVec = ConstantVector::getSplat(vecSize, Constant::getNullValue(Args[0]->getType())); NewVec = InsertElementInst::Create(NewVec, Args[0], getInt32(M, 0), "", CI); NewVec = new ShuffleVectorInst(NewVec, NewVec, ConstantVector::getSplat(vecSize, getInt32(M, 0)), "", CI); } NewVec->takeName(Args[0]); Args[0] = NewVec; return FuncName; }, &Attrs); } return CI; } std::string SPIRVToLLVM::transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST) { return SPIRSPIRVAccessQualifierMap::rmap(ST->getAccessQualifier()); } void SPIRVToLLVM::transGeneratorMD() { SPIRVMDBuilder B(*M); B.addNamedMD(kSPIRVMD::Generator) .addOp() .addU16(BM->getGeneratorId()) .addU16(BM->getGeneratorVer()) .done(); } Value * SPIRVToLLVM::oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS) { auto Lit = (BCS->getAddrMode() << 1) | BCS->getNormalized() | ((BCS->getFilterMode() + 1) << 4); auto Ty = IntegerType::getInt32Ty(*Context); return ConstantInt::get(Ty, Lit); } Value * SPIRVToLLVM::oclTransConstantPipeStorage( SPIRV::SPIRVConstantPipeStorage* BCPS) { string CPSName = string(kSPIRVTypeName::PrefixAndDelim) + kSPIRVTypeName::ConstantPipeStorage; auto Int32Ty = IntegerType::getInt32Ty(*Context); auto CPSTy = M->getTypeByName(CPSName); if (!CPSTy) { Type* CPSElemsTy[] = { Int32Ty, Int32Ty, Int32Ty }; CPSTy = StructType::create(*Context, CPSElemsTy, CPSName); } assert(CPSTy != nullptr && "Could not create spirv.ConstantPipeStorage"); Constant* CPSElems[] = { ConstantInt::get(Int32Ty, BCPS->getPacketSize()), ConstantInt::get(Int32Ty, BCPS->getPacketAlign()), ConstantInt::get(Int32Ty, BCPS->getCapacity()) }; return new GlobalVariable(*M, CPSTy, false, GlobalValue::LinkOnceODRLinkage, ConstantStruct::get(CPSTy, CPSElems), BCPS->getName(), nullptr, GlobalValue::NotThreadLocal, SPIRAS_Global); } /// For instructions, this function assumes they are created in order /// and appended to the given basic block. An instruction may use a /// instruction from another BB which has not been translated. Such /// instructions should be translated to place holders at the point /// of first use, then replaced by real instructions when they are /// created. /// /// When CreatePlaceHolder is true, create a load instruction of a /// global variable as placeholder for SPIRV instruction. Otherwise, /// create instruction and replace placeholder if there is one. Value * SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, BasicBlock *BB, bool CreatePlaceHolder){ auto OC = BV->getOpCode(); IntBoolOpMap::rfind(OC, &OC); // Translation of non-instruction values switch(OC) { case OpConstant: { SPIRVConstant *BConst = static_cast(BV); SPIRVType *BT = BV->getType(); Type *LT = transType(BT); switch(BT->getOpCode()) { case OpTypeBool: case OpTypeInt: return mapValue(BV, ConstantInt::get(LT, BConst->getZExtIntValue(), static_cast(BT)->isSigned())); case OpTypeFloat: { const llvm::fltSemantics *FS = nullptr; switch (BT->getFloatBitWidth()) { case 16: FS = &APFloat::IEEEhalf; break; case 32: FS = &APFloat::IEEEsingle; break; case 64: FS = &APFloat::IEEEdouble; break; default: llvm_unreachable("invalid float type"); } return mapValue(BV, ConstantFP::get(*Context, APFloat(*FS, APInt(BT->getFloatBitWidth(), BConst->getZExtIntValue())))); } default: llvm_unreachable("Not implemented"); return nullptr; } } case OpConstantTrue: return mapValue(BV, ConstantInt::getTrue(*Context)); case OpConstantFalse: return mapValue(BV, ConstantInt::getFalse(*Context)); case OpConstantNull: { auto LT = transType(BV->getType()); return mapValue(BV, Constant::getNullValue(LT)); } case OpConstantComposite: { auto BCC = static_cast(BV); std::vector CV; for (auto &I:BCC->getElements()) CV.push_back(dyn_cast(transValue(I, F, BB))); switch(BV->getType()->getOpCode()) { case OpTypeVector: return mapValue(BV, ConstantVector::get(CV)); case OpTypeArray: return mapValue(BV, ConstantArray::get( dyn_cast(transType(BCC->getType())), CV)); case OpTypeStruct: { auto BCCTy = dyn_cast(transType(BCC->getType())); auto Members = BCCTy->getNumElements(); auto Constants = CV.size(); //if we try to initialize constant TypeStruct, add bitcasts //if src and dst types are both pointers but to different types if (Members == Constants) { for (unsigned i = 0; i < Members; ++i) { if (CV[i]->getType() == BCCTy->getElementType(i)) continue; if (!CV[i]->getType()->isPointerTy() || !BCCTy->getElementType(i)->isPointerTy()) continue; CV[i] = ConstantExpr::getBitCast(CV[i], BCCTy->getElementType(i)); } } return mapValue(BV, ConstantStruct::get( dyn_cast(transType(BCC->getType())), CV)); } default: llvm_unreachable("not implemented"); return nullptr; } } case OpConstantSampler: { auto BCS = static_cast(BV); return mapValue(BV, oclTransConstantSampler(BCS)); } case OpConstantPipeStorage: { auto BCPS = static_cast(BV); return mapValue(BV, oclTransConstantPipeStorage(BCPS)); } case OpSpecConstantOp: { auto BI = createInstFromSpecConstantOp( static_cast(BV)); return mapValue(BV, transValue(BI, nullptr, nullptr, false)); } case OpUndef: return mapValue(BV, UndefValue::get(transType(BV->getType()))); case OpVariable: { auto BVar = static_cast(BV); auto Ty = transType(BVar->getType()->getPointerElementType()); bool IsConst = BVar->isConstant(); llvm::GlobalValue::LinkageTypes LinkageTy = transLinkageType(BVar); Constant *Initializer = nullptr; SPIRVValue *Init = BVar->getInitializer(); if (Init) Initializer = dyn_cast(transValue(Init, F, BB, false)); else if (LinkageTy == GlobalValue::CommonLinkage) // In LLVM variables with common linkage type must be initilized by 0 Initializer = Constant::getNullValue(Ty); SPIRVStorageClassKind BS = BVar->getStorageClass(); if (BS == StorageClassFunction && !Init) { assert (BB && "Invalid BB"); return mapValue(BV, new AllocaInst(Ty, BV->getName(), BB)); } auto AddrSpace = SPIRSPIRVAddrSpaceMap::rmap(BS); auto LVar = new GlobalVariable(*M, Ty, IsConst, LinkageTy, Initializer, BV->getName(), 0, GlobalVariable::NotThreadLocal, AddrSpace); LVar->setUnnamedAddr((IsConst && Ty->isArrayTy() && Ty->getArrayElementType()->isIntegerTy(8)) ? GlobalValue::UnnamedAddr::Global : GlobalValue::UnnamedAddr::None); SPIRVBuiltinVariableKind BVKind; if (BVar->isBuiltin(&BVKind)) BuiltinGVMap[LVar] = BVKind; return mapValue(BV, LVar); } case OpFunctionParameter: { auto BA = static_cast(BV); assert (F && "Invalid function"); unsigned ArgNo = 0; for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I, ++ArgNo) { if (ArgNo == BA->getArgNo()) return mapValue(BV, static_cast(I)); } llvm_unreachable("Invalid argument"); return nullptr; } case OpFunction: return mapValue(BV, transFunction(static_cast(BV))); case OpLabel: return mapValue(BV, BasicBlock::Create(*Context, BV->getName(), F)); case OpBitcast: // Can be translated without BB pointer if(!CreatePlaceHolder) // May be a placeholder return mapValue(BV, transConvertInst(BV, F, BB)); default: // do nothing break; } // All other values require valid BB pointer. assert(BB && "Invalid BB"); // Creation of place holder if (CreatePlaceHolder) { auto GV = new GlobalVariable(*M, transType(BV->getType()), false, GlobalValue::PrivateLinkage, nullptr, std::string(kPlaceholderPrefix) + BV->getName(), 0, GlobalVariable::NotThreadLocal, 0); auto LD = new LoadInst(GV, BV->getName(), BB); PlaceholderMap[BV] = LD; return mapValue(BV, LD); } // Translation of instructions switch (BV->getOpCode()) { case OpBranch: { auto BR = static_cast(BV); return mapValue(BV, BranchInst::Create( dyn_cast(transValue(BR->getTargetLabel(), F, BB)), BB)); } case OpBranchConditional: { auto BR = static_cast(BV); return mapValue( BV, BranchInst::Create( dyn_cast(transValue(BR->getTrueLabel(), F, BB)), dyn_cast(transValue(BR->getFalseLabel(), F, BB)), transValue(BR->getCondition(), F, BB), BB)); } case OpPhi: { auto Phi = static_cast(BV); auto LPhi = dyn_cast(mapValue( BV, PHINode::Create(transType(Phi->getType()), Phi->getPairs().size() / 2, Phi->getName(), BB))); Phi->foreachPair([&](SPIRVValue *IncomingV, SPIRVBasicBlock *IncomingBB, size_t Index) { auto Translated = transValue(IncomingV, F, BB); LPhi->addIncoming(Translated, dyn_cast(transValue(IncomingBB, F, BB))); }); return LPhi; } case OpReturn: return mapValue(BV, ReturnInst::Create(*Context, BB)); case OpReturnValue: { auto RV = static_cast(BV); return mapValue( BV, ReturnInst::Create(*Context, transValue(RV->getReturnValue(), F, BB), BB)); } case OpStore: { SPIRVStore *BS = static_cast(BV); StoreInst *SI = new StoreInst(transValue(BS->getSrc(), F, BB), transValue(BS->getDst(), F, BB), BS->SPIRVMemoryAccess::isVolatile(), BS->SPIRVMemoryAccess::getAlignment(), BB); if (BS->SPIRVMemoryAccess::isNonTemporal()) transNonTemporalMetadata(SI); return mapValue(BV, SI); } case OpLoad: { SPIRVLoad *BL = static_cast(BV); LoadInst *LI = new LoadInst(transValue(BL->getSrc(), F, BB), BV->getName(), BL->SPIRVMemoryAccess::isVolatile(), BL->SPIRVMemoryAccess::getAlignment(), BB); if (BL->SPIRVMemoryAccess::isNonTemporal()) transNonTemporalMetadata(LI); return mapValue(BV, LI); } case OpCopyMemorySized: { SPIRVCopyMemorySized *BC = static_cast(BV); std::string FuncName = "llvm.memcpy"; SPIRVType* BS = BC->getSource()->getType(); SPIRVType* BT = BC->getTarget()->getType(); Type *Int1Ty = Type::getInt1Ty(*Context); Type* Int32Ty = Type::getInt32Ty(*Context); Type* VoidTy = Type::getVoidTy(*Context); Type* SrcTy = transType(BS); Type* TrgTy = transType(BT); Type* SizeTy = transType(BC->getSize()->getType()); Type* ArgTy[] = { TrgTy, SrcTy, SizeTy, Int32Ty, Int1Ty }; ostringstream TempName; TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BT->getPointerStorageClass()) << "i8"; TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BS->getPointerStorageClass()) << "i8"; FuncName += TempName.str(); if (BC->getSize()->getType()->getBitWidth() == 32) FuncName += ".i32"; else FuncName += ".i64"; FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); Function *Func = dyn_cast(M->getOrInsertFunction(FuncName, FT)); assert(Func && Func->getFunctionType() == FT && "Function type mismatch"); Func->setLinkage(GlobalValue::ExternalLinkage); if (isFuncNoUnwind()) Func->addFnAttr(Attribute::NoUnwind); Value *Arg[] = { transValue(BC->getTarget(), Func, BB), transValue(BC->getSource(), Func, BB), dyn_cast(transValue(BC->getSize(), Func, BB)), ConstantInt::get(Int32Ty, BC->SPIRVMemoryAccess::getAlignment()), ConstantInt::get(Int1Ty, BC->SPIRVMemoryAccess::isVolatile())}; return mapValue( BV, CallInst::Create(Func, Arg, "", BB)); } case OpSelect: { SPIRVSelect *BS = static_cast(BV); return mapValue(BV, SelectInst::Create(transValue(BS->getCondition(), F, BB), transValue(BS->getTrueValue(), F, BB), transValue(BS->getFalseValue(), F, BB), BV->getName(), BB)); } case OpSwitch: { auto BS = static_cast(BV); auto Select = transValue(BS->getSelect(), F, BB); auto LS = SwitchInst::Create( Select, dyn_cast(transValue(BS->getDefault(), F, BB)), BS->getNumPairs(), BB); BS->foreachPair( [&](SPIRVWord Literal, SPIRVBasicBlock *Label, size_t Index) { LS->addCase(ConstantInt::get(dyn_cast(Select->getType()), Literal), dyn_cast(transValue(Label, F, BB))); }); return mapValue(BV, LS); } case OpAccessChain: case OpInBoundsAccessChain: case OpPtrAccessChain: case OpInBoundsPtrAccessChain: { auto AC = static_cast(BV); auto Base = transValue(AC->getBase(), F, BB); auto Index = transValue(AC->getIndices(), F, BB); if (!AC->hasPtrIndex()) Index.insert(Index.begin(), getInt32(M, 0)); auto IsInbound = AC->isInBounds(); Value *V = nullptr; if (BB) { auto GEP = GetElementPtrInst::Create(nullptr, Base, Index, BV->getName(), BB); GEP->setIsInBounds(IsInbound); V = GEP; } else { V = ConstantExpr::getGetElementPtr(Base->getType(), dyn_cast(Base), Index, IsInbound); } return mapValue(BV, V); } case OpCompositeExtract: { SPIRVCompositeExtract *CE = static_cast(BV); if (CE->getComposite()->getType()->isTypeVector()) { assert(CE->getIndices().size() == 1 && "Invalid index"); return mapValue( BV, ExtractElementInst::Create( transValue(CE->getComposite(), F, BB), ConstantInt::get(*Context, APInt(32, CE->getIndices()[0])), BV->getName(), BB)); } return mapValue( BV, ExtractValueInst::Create( transValue(CE->getComposite(), F, BB), CE->getIndices(), BV->getName(), BB)); } case OpVectorExtractDynamic: { auto CE = static_cast(BV); return mapValue( BV, ExtractElementInst::Create(transValue(CE->getVector(), F, BB), transValue(CE->getIndex(), F, BB), BV->getName(), BB)); } case OpCompositeInsert: { auto CI = static_cast(BV); if (CI->getComposite()->getType()->isTypeVector()) { assert(CI->getIndices().size() == 1 && "Invalid index"); return mapValue( BV, InsertElementInst::Create( transValue(CI->getComposite(), F, BB), transValue(CI->getObject(), F, BB), ConstantInt::get(*Context, APInt(32, CI->getIndices()[0])), BV->getName(), BB)); } return mapValue( BV, InsertValueInst::Create( transValue(CI->getComposite(), F, BB), transValue(CI->getObject(), F, BB), CI->getIndices(), BV->getName(), BB)); } case OpVectorInsertDynamic: { auto CI = static_cast(BV); return mapValue( BV, InsertElementInst::Create(transValue(CI->getVector(), F, BB), transValue(CI->getComponent(), F, BB), transValue(CI->getIndex(), F, BB), BV->getName(), BB)); } case OpVectorShuffle: { auto VS = static_cast(BV); std::vector Components; IntegerType *Int32Ty = IntegerType::get(*Context, 32); for (auto I : VS->getComponents()) { if (I == static_cast(-1)) Components.push_back(UndefValue::get(Int32Ty)); else Components.push_back(ConstantInt::get(Int32Ty, I)); } return mapValue(BV, new ShuffleVectorInst(transValue(VS->getVector1(), F, BB), transValue(VS->getVector2(), F, BB), ConstantVector::get(Components), BV->getName(), BB)); } case OpFunctionCall: { SPIRVFunctionCall *BC = static_cast(BV); auto Call = CallInst::Create(transFunction(BC->getFunction()), transValue(BC->getArgumentValues(), F, BB), BC->getName(), BB); setCallingConv(Call); setAttrByCalledFunc(Call); return mapValue(BV, Call); } case OpExtInst: return mapValue( BV, transOCLBuiltinFromExtInst(static_cast(BV), BB)); case OpControlBarrier: case OpMemoryBarrier: return mapValue( BV, transOCLBarrierFence(static_cast(BV), BB)); case OpSNegate: { SPIRVUnary *BC = static_cast(BV); return mapValue( BV, BinaryOperator::CreateNSWNeg(transValue(BC->getOperand(0), F, BB), BV->getName(), BB)); } case OpFNegate: { SPIRVUnary *BC = static_cast(BV); return mapValue( BV, BinaryOperator::CreateFNeg(transValue(BC->getOperand(0), F, BB), BV->getName(), BB)); } case OpNot: { SPIRVUnary *BC = static_cast(BV); return mapValue( BV, BinaryOperator::CreateNot(transValue(BC->getOperand(0), F, BB), BV->getName(), BB)); } case OpAll : case OpAny : return mapValue(BV, transOCLAllAny(static_cast(BV), BB)); case OpIsFinite : case OpIsInf : case OpIsNan : case OpIsNormal : case OpSignBitSet : return mapValue(BV, transOCLRelational(static_cast(BV), BB)); default: { auto OC = BV->getOpCode(); if (isSPIRVCmpInstTransToLLVMInst(static_cast(BV))) { return mapValue(BV, transCmpInst(BV, BB, F)); } else if (OCLSPIRVBuiltinMap::rfind(OC, nullptr) && !isAtomicOpCode(OC) && !isGroupOpCode(OC) && !isPipeOpCode(OC)) { return mapValue(BV, transOCLBuiltinFromInst( static_cast(BV), BB)); } else if (isBinaryShiftLogicalBitwiseOpCode(OC) || isLogicalOpCode(OC)) { return mapValue(BV, transShiftLogicalBitwiseInst(BV, BB, F)); } else if (isCvtOpCode(OC)) { auto BI = static_cast(BV); Value *Inst = nullptr; if (BI->hasFPRoundingMode() || BI->isSaturatedConversion()) Inst = transOCLBuiltinFromInst(BI, BB); else Inst = transConvertInst(BV, F, BB); return mapValue(BV, Inst); } return mapValue(BV, transSPIRVBuiltinFromInst( static_cast(BV), BB)); } SPIRVDBG(spvdbgs() << "Cannot translate " << *BV << '\n';) llvm_unreachable("Translation of SPIRV instruction not implemented"); return NULL; } } template bool SPIRVToLLVM::foreachFuncCtlMask(SourceTy Source, FuncTy Func) { SPIRVWord FCM = Source->getFuncCtlMask(); SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, SPIRVFunctionControlMaskKind Mask){ if (FCM & Mask) Func(Attr); }); return true; } Function * SPIRVToLLVM::transFunction(SPIRVFunction *BF) { auto Loc = FuncMap.find(BF); if (Loc != FuncMap.end()) return Loc->second; auto IsKernel = BM->isEntryPoint(ExecutionModelKernel, BF->getId()); auto Linkage = IsKernel ? GlobalValue::ExternalLinkage : transLinkageType(BF); FunctionType *FT = dyn_cast(transType(BF->getFunctionType())); Function *F = dyn_cast(mapValue(BF, Function::Create(FT, Linkage, BF->getName(), M))); assert(F); mapFunction(BF, F); if (!F->isIntrinsic()) { F->setCallingConv(IsKernel ? CallingConv::SPIR_KERNEL : CallingConv::SPIR_FUNC); if (isFuncNoUnwind()) F->addFnAttr(Attribute::NoUnwind); foreachFuncCtlMask(BF, [&](Attribute::AttrKind Attr){ F->addFnAttr(Attr); }); } for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I) { auto BA = BF->getArgument(I->getArgNo()); mapValue(BA, static_cast(I)); setName(static_cast(I), BA); BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind){ if (Kind == FunctionParameterAttributeNoWrite) return; F->addAttribute(I->getArgNo() + 1, SPIRSPIRVFuncParamAttrMap::rmap(Kind)); }); SPIRVWord MaxOffset = 0; if (BA->hasDecorate(DecorationMaxByteOffset, 0, &MaxOffset)) { AttrBuilder Builder; Builder.addDereferenceableAttr(MaxOffset); I->addAttr(AttributeSet::get(*Context, I->getArgNo() + 1, Builder)); } } BF->foreachReturnValueAttr([&](SPIRVFuncParamAttrKind Kind){ if (Kind == FunctionParameterAttributeNoWrite) return; F->addAttribute(AttributeSet::ReturnIndex, SPIRSPIRVFuncParamAttrMap::rmap(Kind)); }); // Creating all basic blocks before creating instructions. for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) { transValue(BF->getBasicBlock(I), F, nullptr); } for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) { SPIRVBasicBlock *BBB = BF->getBasicBlock(I); BasicBlock *BB = dyn_cast(transValue(BBB, F, nullptr)); for (size_t BI = 0, BE = BBB->getNumInst(); BI != BE; ++BI) { SPIRVInstruction *BInst = BBB->getInst(BI); transValue(BInst, F, BB, false); } } return F; } /// LLVM convert builtin functions is translated to two instructions: /// y = i32 islessgreater(float x, float z) -> /// y = i32 ZExt(bool LessGreater(float x, float z)) /// When translating back, for simplicity, a trunc instruction is inserted /// w = bool LessGreater(float x, float z) -> /// w = bool Trunc(i32 islessgreater(float x, float z)) /// Optimizer should be able to remove the redundant trunc/zext void SPIRVToLLVM::transOCLBuiltinFromInstPreproc(SPIRVInstruction* BI, Type *&RetTy, std::vector &Args) { if (!BI->hasType()) return; auto BT = BI->getType(); auto OC = BI->getOpCode(); if (isCmpOpCode(BI->getOpCode())) { if (BT->isTypeBool()) RetTy = IntegerType::getInt32Ty(*Context); else if (BT->isTypeVectorBool()) RetTy = VectorType::get(IntegerType::get(*Context, Args[0]->getType()->getVectorComponentType()->isTypeFloat(64)?64:32), BT->getVectorComponentCount()); else llvm_unreachable("invalid compare instruction"); } else if (OC == OpGenericCastToPtrExplicit) Args.pop_back(); else if (OC == OpImageRead && Args.size() > 2) { // Drop "Image operands" argument Args.erase(Args.begin() + 2); } } Instruction* SPIRVToLLVM::transOCLBuiltinPostproc(SPIRVInstruction* BI, CallInst* CI, BasicBlock* BB, const std::string &DemangledName) { auto OC = BI->getOpCode(); if (isCmpOpCode(OC) && BI->getType()->isTypeVectorOrScalarBool()) { return CastInst::Create(Instruction::Trunc, CI, transType(BI->getType()), "cvt", BB); } if (OC == OpImageSampleExplicitLod) return postProcessOCLReadImage(BI, CI, DemangledName); if (OC == OpImageWrite) { return postProcessOCLWriteImage(BI, CI, DemangledName); } if (OC == OpGenericPtrMemSemantics) return BinaryOperator::CreateShl(CI, getInt32(M, 8), "", BB); if (OC == OpImageQueryFormat) return BinaryOperator::CreateSub( CI, getInt32(M, OCLImageChannelDataTypeOffset), "", BB); if (OC == OpImageQueryOrder) return BinaryOperator::CreateSub( CI, getInt32(M, OCLImageChannelOrderOffset), "", BB); if (OC == OpBuildNDRange) return postProcessOCLBuildNDRange(BI, CI, DemangledName); if (OC == OpGroupAll || OC == OpGroupAny) return postProcessGroupAllAny(CI, DemangledName); if (SPIRVEnableStepExpansion && (DemangledName == "smoothstep" || DemangledName == "step")) return expandOCLBuiltinWithScalarArg(CI, DemangledName); return CI; } Instruction * SPIRVToLLVM::transBuiltinFromInst(const std::string& FuncName, SPIRVInstruction* BI, BasicBlock* BB) { std::string MangledName; auto Ops = BI->getOperands(); Type* RetTy = BI->hasType() ? transType(BI->getType()) : Type::getVoidTy(*Context); transOCLBuiltinFromInstPreproc(BI, RetTy, Ops); std::vector ArgTys = transTypeVector( SPIRVInstruction::getOperandTypes(Ops)); bool HasFuncPtrArg = false; for (auto& I:ArgTys) { if (isa(I)) { I = PointerType::get(I, SPIRAS_Private); HasFuncPtrArg = true; } } if (!HasFuncPtrArg) MangleOpenCLBuiltin(FuncName, ArgTys, MangledName); else MangledName = decorateSPIRVFunction(FuncName); Function* Func = M->getFunction(MangledName); FunctionType* FT = FunctionType::get(RetTy, ArgTys, false); // ToDo: Some intermediate functions have duplicate names with // different function types. This is OK if the function name // is used internally and finally translated to unique function // names. However it is better to have a way to differentiate // between intermidiate functions and final functions and make // sure final functions have unique names. SPIRVDBG( if (!HasFuncPtrArg && Func && Func->getFunctionType() != FT) { dbgs() << "Warning: Function name conflict:\n" << *Func << '\n' << " => " << *FT << '\n'; } ) if (!Func || Func->getFunctionType() != FT) { DEBUG(for (auto& I:ArgTys) { dbgs() << *I << '\n'; }); Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); Func->setCallingConv(CallingConv::SPIR_FUNC); if (isFuncNoUnwind()) Func->addFnAttr(Attribute::NoUnwind); } auto Call = CallInst::Create(Func, transValue(Ops, BB->getParent(), BB), "", BB); setName(Call, BI); setAttrByCalledFunc(Call); SPIRVDBG(spvdbgs() << "[transInstToBuiltinCall] " << *BI << " -> "; dbgs() << *Call << '\n';) Instruction *Inst = Call; Inst = transOCLBuiltinPostproc(BI, Call, BB, FuncName); return Inst; } std::string SPIRVToLLVM::getOCLBuiltinName(SPIRVInstruction* BI) { auto OC = BI->getOpCode(); if (OC == OpGenericCastToPtrExplicit) return getOCLGenericCastToPtrName(BI); if (isCvtOpCode(OC)) return getOCLConvertBuiltinName(BI); if (OC == OpBuildNDRange) { auto NDRangeInst = static_cast(BI); auto EleTy = ((NDRangeInst->getOperands())[0])->getType(); int Dim = EleTy->isTypeArray() ? EleTy->getArrayLength() : 1; // cygwin does not have std::to_string ostringstream OS; OS << Dim; assert((EleTy->isTypeInt() && Dim == 1) || (EleTy->isTypeArray() && Dim >= 2 && Dim <= 3)); return std::string(kOCLBuiltinName::NDRangePrefix) + OS.str() + "D"; } auto Name = OCLSPIRVBuiltinMap::rmap(OC); SPIRVType *T = nullptr; switch(OC) { case OpImageRead: T = BI->getType(); break; case OpImageWrite: T = BI->getOperands()[2]->getType(); break; default: // do nothing break; } if (T && T->isTypeVector()) T = T->getVectorComponentType(); if (T) Name += T->isTypeFloat()?'f':'i'; return Name; } Instruction * SPIRVToLLVM::transOCLBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB) { assert(BB && "Invalid BB"); auto FuncName = getOCLBuiltinName(BI); return transBuiltinFromInst(FuncName, BI, BB); } Instruction * SPIRVToLLVM::transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB) { assert(BB && "Invalid BB"); string Suffix = ""; if (BI->getOpCode() == OpCreatePipeFromPipeStorage) { auto CPFPS = static_cast(BI); assert(CPFPS->getType()->isTypePipe() && "Invalid type of CreatePipeFromStorage"); auto PipeType = static_cast(CPFPS->getType()); switch (PipeType->getAccessQualifier()) { case AccessQualifierReadOnly: Suffix = "_read"; break; case AccessQualifierWriteOnly: Suffix = "_write"; break; case AccessQualifierReadWrite: Suffix = "_read_write"; break; } } return transBuiltinFromInst(getSPIRVFuncName(BI->getOpCode(), Suffix), BI, BB); } bool SPIRVToLLVM::translate() { if (!transAddressingModel()) return false; DbgTran.createCompileUnit(); DbgTran.addDbgInfoVersion(); for (unsigned I = 0, E = BM->getNumVariables(); I != E; ++I) { auto BV = BM->getVariable(I); if (BV->getStorageClass() != StorageClassFunction) transValue(BV, nullptr, nullptr); } for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { transFunction(BM->getFunction(I)); } if (!transKernelMetadata()) return false; if (!transFPContractMetadata()) return false; if (!transSourceLanguage()) return false; if (!transSourceExtension()) return false; transGeneratorMD(); if (!transOCLBuiltinsFromVariables()) return false; if (!postProcessOCL()) return false; eraseUselessFunctions(M); DbgTran.finalize(); return true; } bool SPIRVToLLVM::transAddressingModel() { switch (BM->getAddressingModel()) { case AddressingModelPhysical64: M->setTargetTriple(SPIR_TARGETTRIPLE64); M->setDataLayout(SPIR_DATALAYOUT64); break; case AddressingModelPhysical32: M->setTargetTriple(SPIR_TARGETTRIPLE32); M->setDataLayout(SPIR_DATALAYOUT32); break; case AddressingModelLogical: // Do not set target triple and data layout break; default: SPIRVCKRT(0, InvalidAddressingModel, "Actual addressing mode is " + (unsigned)BM->getAddressingModel()); } return true; } bool SPIRVToLLVM::transDecoration(SPIRVValue *BV, Value *V) { if (!transAlign(BV, V)) return false; DbgTran.transDbgInfo(BV, V); return true; } bool SPIRVToLLVM::transFPContractMetadata() { bool ContractOff = false; for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { SPIRVFunction *BF = BM->getFunction(I); if (!isOpenCLKernel(BF)) continue; if (BF->getExecutionMode(ExecutionModeContractionOff)) { ContractOff = true; break; } } if (!ContractOff) M->getOrInsertNamedMetadata(kSPIR2MD::FPContract); return true; } std::string SPIRVToLLVM::transOCLImageTypeAccessQualifier( SPIRV::SPIRVTypeImage* ST) { return SPIRSPIRVAccessQualifierMap::rmap(ST->getAccessQualifier()); } bool SPIRVToLLVM::transNonTemporalMetadata(Instruction *I) { Constant* One = ConstantInt::get(Type::getInt32Ty(*Context), 1); MDNode *Node = MDNode::get(*Context, ConstantAsMetadata::get(One)); I->setMetadata(M->getMDKindID("nontemporal"), Node); return true; } bool SPIRVToLLVM::transKernelMetadata() { NamedMDNode *KernelMDs = M->getOrInsertNamedMetadata(SPIR_MD_KERNELS); for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { SPIRVFunction *BF = BM->getFunction(I); Function *F = static_cast(getTranslatedValue(BF)); assert(F && "Invalid translated function"); if (F->getCallingConv() != CallingConv::SPIR_KERNEL) continue; std::vector KernelMD; KernelMD.push_back(ValueAsMetadata::get(F)); // Generate metadata for kernel_arg_address_spaces addOCLKernelArgumentMetadata(Context, KernelMD, SPIR_MD_KERNEL_ARG_ADDR_SPACE, BF, [=](SPIRVFunctionParameter *Arg){ SPIRVType *ArgTy = Arg->getType(); SPIRAddressSpace AS = SPIRAS_Private; if (ArgTy->isTypePointer()) AS = SPIRSPIRVAddrSpaceMap::rmap(ArgTy->getPointerStorageClass()); else if (ArgTy->isTypeOCLImage() || ArgTy->isTypePipe()) AS = SPIRAS_Global; return ConstantAsMetadata::get( ConstantInt::get(Type::getInt32Ty(*Context), AS)); }); // Generate metadata for kernel_arg_access_qual addOCLKernelArgumentMetadata(Context, KernelMD, SPIR_MD_KERNEL_ARG_ACCESS_QUAL, BF, [=](SPIRVFunctionParameter *Arg){ std::string Qual; auto T = Arg->getType(); if (T->isTypeOCLImage()) { auto ST = static_cast(T); Qual = transOCLImageTypeAccessQualifier(ST); } else if (T->isTypePipe()){ auto PT = static_cast(T); Qual = transOCLPipeTypeAccessQualifier(PT); } else Qual = "none"; return MDString::get(*Context, Qual); }); // Generate metadata for kernel_arg_type addOCLKernelArgumentMetadata(Context, KernelMD, SPIR_MD_KERNEL_ARG_TYPE, BF, [=](SPIRVFunctionParameter *Arg){ return transOCLKernelArgTypeName(Arg); }); // Generate metadata for kernel_arg_type_qual addOCLKernelArgumentMetadata(Context, KernelMD, SPIR_MD_KERNEL_ARG_TYPE_QUAL, BF, [=](SPIRVFunctionParameter *Arg){ std::string Qual; if (Arg->hasDecorate(DecorationVolatile)) Qual = kOCLTypeQualifierName::Volatile; Arg->foreachAttr([&](SPIRVFuncParamAttrKind Kind){ Qual += Qual.empty() ? "" : " "; switch(Kind){ case FunctionParameterAttributeNoAlias: Qual += kOCLTypeQualifierName::Restrict; break; case FunctionParameterAttributeNoWrite: Qual += kOCLTypeQualifierName::Const; break; default: // do nothing. break; } }); if (Arg->getType()->isTypePipe()) { Qual += Qual.empty() ? "" : " "; Qual += kOCLTypeQualifierName::Pipe; } return MDString::get(*Context, Qual); }); // Generate metadata for kernel_arg_base_type addOCLKernelArgumentMetadata(Context, KernelMD, SPIR_MD_KERNEL_ARG_BASE_TYPE, BF, [=](SPIRVFunctionParameter *Arg){ return transOCLKernelArgTypeName(Arg); }); // Generate metadata for kernel_arg_name if (SPIRVGenKernelArgNameMD) { bool ArgHasName = true; BF->foreachArgument([&](SPIRVFunctionParameter *Arg){ ArgHasName &= !Arg->getName().empty(); }); if (ArgHasName) addOCLKernelArgumentMetadata(Context, KernelMD, SPIR_MD_KERNEL_ARG_NAME, BF, [=](SPIRVFunctionParameter *Arg){ return MDString::get(*Context, Arg->getName()); }); } // Generate metadata for reqd_work_group_size if (auto EM = BF->getExecutionMode(ExecutionModeLocalSize)) { KernelMD.push_back(getMDNodeStringIntVec(Context, kSPIR2MD::WGSize, EM->getLiterals())); } // Generate metadata for work_group_size_hint if (auto EM = BF->getExecutionMode(ExecutionModeLocalSizeHint)) { KernelMD.push_back(getMDNodeStringIntVec(Context, kSPIR2MD::WGSizeHint, EM->getLiterals())); } // Generate metadata for vec_type_hint if (auto EM = BF->getExecutionMode(ExecutionModeVecTypeHint)) { std::vector MetadataVec; MetadataVec.push_back(MDString::get(*Context, kSPIR2MD::VecTyHint)); Type *VecHintTy = decodeVecTypeHint(*Context, EM->getLiterals()[0]); assert(VecHintTy); MetadataVec.push_back(ValueAsMetadata::get(UndefValue::get(VecHintTy))); MetadataVec.push_back( ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), 1))); KernelMD.push_back(MDNode::get(*Context, MetadataVec)); } llvm::MDNode *Node = MDNode::get(*Context, KernelMD); KernelMDs->addOperand(Node); } return true; } bool SPIRVToLLVM::transAlign(SPIRVValue *BV, Value *V) { if (auto AL = dyn_cast(V)) { SPIRVWord Align = 0; if (BV->hasAlignment(&Align)) AL->setAlignment(Align); return true; } if (auto GV = dyn_cast(V)) { SPIRVWord Align = 0; if (BV->hasAlignment(&Align)) GV->setAlignment(Align); return true; } return true; } void SPIRVToLLVM::transOCLVectorLoadStore(std::string& UnmangledName, std::vector &BArgs) { if (UnmangledName.find("vload") == 0 && UnmangledName.find("n") != std::string::npos) { if (BArgs.back() != 1) { std::stringstream SS; SS << BArgs.back(); UnmangledName.replace(UnmangledName.find("n"), 1, SS.str()); } else { UnmangledName.erase(UnmangledName.find("n"), 1); } BArgs.pop_back(); } else if (UnmangledName.find("vstore") == 0) { if (UnmangledName.find("n") != std::string::npos) { auto T = BM->getValueType(BArgs[0]); if (T->isTypeVector()) { auto W = T->getVectorComponentCount(); std::stringstream SS; SS << W; UnmangledName.replace(UnmangledName.find("n"), 1, SS.str()); } else { UnmangledName.erase(UnmangledName.find("n"), 1); } } if (UnmangledName.find("_r") != std::string::npos) { UnmangledName.replace(UnmangledName.find("_r"), 2, std::string("_") + SPIRSPIRVFPRoundingModeMap::rmap(static_cast( BArgs.back()))); BArgs.pop_back(); } } } // printf is not mangled. The function type should have just one argument. // read_image*: the second argument should be mangled as sampler. Instruction * SPIRVToLLVM::transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB) { assert(BB && "Invalid BB"); std::string MangledName; SPIRVWord EntryPoint = BC->getExtOp(); SPIRVExtInstSetKind Set = BM->getBuiltinSet(BC->getExtSetId()); bool IsVarArg = false; bool IsPrintf = false; std::string UnmangledName; auto BArgs = BC->getArguments(); (void) Set; assert (Set == SPIRVEIS_OpenCL && "Not OpenCL extended instruction"); if (EntryPoint == OpenCLLIB::Printf) IsPrintf = true; else { UnmangledName = OCLExtOpMap::map(static_cast( EntryPoint)); } SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] OrigUnmangledName: " << UnmangledName << '\n'); transOCLVectorLoadStore(UnmangledName, BArgs); std::vector ArgTypes = transTypeVector(BC->getValueTypes(BArgs)); if (IsPrintf) { MangledName = "printf"; IsVarArg = true; ArgTypes.resize(1); } else if (UnmangledName.find("read_image") == 0) { auto ModifiedArgTypes = ArgTypes; ModifiedArgTypes[1] = getOrCreateOpaquePtrType(M, "opencl.sampler_t"); MangleOpenCLBuiltin(UnmangledName, ModifiedArgTypes, MangledName); } else { MangleOpenCLBuiltin(UnmangledName, ArgTypes, MangledName); } SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] ModifiedUnmangledName: " << UnmangledName << " MangledName: " << MangledName << '\n'); FunctionType *FT = FunctionType::get( transType(BC->getType()), ArgTypes, IsVarArg); Function *F = M->getFunction(MangledName); if (!F) { F = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); F->setCallingConv(CallingConv::SPIR_FUNC); if (isFuncNoUnwind()) F->addFnAttr(Attribute::NoUnwind); } auto Args = transValue(BC->getValues(BArgs), F, BB); SPIRVDBG(dbgs() << "[transOCLBuiltinFromExtInst] Function: " << *F << ", Args: "; for (auto &I:Args) dbgs() << *I << ", "; dbgs() << '\n'); CallInst *Call = CallInst::Create(F, Args, BC->getName(), BB); setCallingConv(Call); addFnAttr(Context, Call, Attribute::NoUnwind); return transOCLBuiltinPostproc(BC, Call, BB, UnmangledName); } CallInst * SPIRVToLLVM::transOCLBarrier(BasicBlock *BB, SPIRVWord ExecScope, SPIRVWord MemSema, SPIRVWord MemScope) { SPIRVWord Ver = 0; BM->getSourceLanguage(&Ver); Type* Int32Ty = Type::getInt32Ty(*Context); Type* VoidTy = Type::getVoidTy(*Context); std::string FuncName; SmallVector ArgTy; SmallVector Arg; Constant *MemFenceFlags = ConstantInt::get(Int32Ty, rmapBitMask(MemSema)); FuncName = (ExecScope == ScopeWorkgroup) ? kOCLBuiltinName::WorkGroupBarrier : kOCLBuiltinName::SubGroupBarrier; if (ExecScope == ScopeWorkgroup && Ver > 0 && Ver <= kOCLVer::CL12) { FuncName = kOCLBuiltinName::Barrier; ArgTy.push_back(Int32Ty); Arg.push_back(MemFenceFlags); } else { Constant *Scope = ConstantInt::get(Int32Ty, OCLMemScopeMap::rmap( static_cast(MemScope))); ArgTy.append(2, Int32Ty); Arg.push_back(MemFenceFlags); Arg.push_back(Scope); } std::string MangledName; MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); Function *Func = M->getFunction(MangledName); if (!Func) { FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); Func->setCallingConv(CallingConv::SPIR_FUNC); if (isFuncNoUnwind()) Func->addFnAttr(Attribute::NoUnwind); } return CallInst::Create(Func, Arg, "", BB); } CallInst * SPIRVToLLVM::transOCLMemFence(BasicBlock *BB, SPIRVWord MemSema, SPIRVWord MemScope) { SPIRVWord Ver = 0; BM->getSourceLanguage(&Ver); Type* Int32Ty = Type::getInt32Ty(*Context); Type* VoidTy = Type::getVoidTy(*Context); std::string FuncName; SmallVector ArgTy; SmallVector Arg; Constant *MemFenceFlags = ConstantInt::get(Int32Ty, rmapBitMask(MemSema)); if (Ver > 0 && Ver <= kOCLVer::CL12) { FuncName = kOCLBuiltinName::MemFence; ArgTy.push_back(Int32Ty); Arg.push_back(MemFenceFlags); } else { Constant *Order = ConstantInt::get(Int32Ty, mapSPIRVMemOrderToOCL(MemSema)); Constant *Scope = ConstantInt::get(Int32Ty, OCLMemScopeMap::rmap( static_cast(MemScope))); FuncName = kOCLBuiltinName::AtomicWorkItemFence; ArgTy.append(3, Int32Ty); Arg.push_back(MemFenceFlags); Arg.push_back(Order); Arg.push_back(Scope); } std::string MangledName; MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); Function *Func = M->getFunction(MangledName); if (!Func) { FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); Func->setCallingConv(CallingConv::SPIR_FUNC); if (isFuncNoUnwind()) Func->addFnAttr(Attribute::NoUnwind); } return CallInst::Create(Func, Arg, "", BB); } Instruction * SPIRVToLLVM::transOCLBarrierFence(SPIRVInstruction *MB, BasicBlock *BB) { assert(BB && "Invalid BB"); std::string FuncName; auto getIntVal = [](SPIRVValue *value){ return static_cast(value)->getZExtIntValue(); }; CallInst* Call = nullptr; if (MB->getOpCode() == OpMemoryBarrier) { auto MemB = static_cast(MB); SPIRVWord MemScope = getIntVal(MemB->getOpValue(0)); SPIRVWord MemSema = getIntVal(MemB->getOpValue(1)); Call = transOCLMemFence(BB, MemSema, MemScope); } else if (MB->getOpCode() == OpControlBarrier) { auto CtlB = static_cast(MB); SPIRVWord ExecScope = getIntVal(CtlB->getExecScope()); SPIRVWord MemSema = getIntVal(CtlB->getMemSemantic()); SPIRVWord MemScope = getIntVal(CtlB->getMemScope()); Call = transOCLBarrier(BB, ExecScope, MemSema, MemScope); } else { llvm_unreachable("Invalid instruction"); } setName(Call, MB); setAttrByCalledFunc(Call); SPIRVDBG(spvdbgs() << "[transBarrier] " << *MB << " -> "; dbgs() << *Call << '\n';) return Call; } // SPIR-V only contains language version. Use OpenCL language version as // SPIR version. bool SPIRVToLLVM::transSourceLanguage() { SPIRVWord Ver = 0; SourceLanguage Lang = BM->getSourceLanguage(&Ver); assert((Lang == SourceLanguageOpenCL_C || Lang == SourceLanguageOpenCL_CPP) && "Unsupported source language"); unsigned short Major = 0; unsigned char Minor = 0; unsigned char Rev = 0; std::tie(Major, Minor, Rev) = decodeOCLVer(Ver); SPIRVMDBuilder Builder(*M); Builder.addNamedMD(kSPIRVMD::Source) .addOp() .add(Lang) .add(Ver) .done(); // ToDo: Phasing out usage of old SPIR metadata if (Ver <= kOCLVer::CL12) addOCLVersionMetadata(Context, M, kSPIR2MD::SPIRVer, 1, 2); else addOCLVersionMetadata(Context, M, kSPIR2MD::SPIRVer, 2, 0); addOCLVersionMetadata(Context, M, kSPIR2MD::OCLVer, Major, Minor); return true; } bool SPIRVToLLVM::transSourceExtension() { auto ExtSet = rmap(BM->getExtension()); auto CapSet = rmap(BM->getCapability()); ExtSet.insert(CapSet.begin(), CapSet.end()); auto OCLExtensions = map(ExtSet); std::set OCLOptionalCoreFeatures; static const char *OCLOptCoreFeatureNames[] = { "cl_images", "cl_doubles", }; for (auto &I : OCLOptCoreFeatureNames) { auto Loc = OCLExtensions.find(I); if (Loc != OCLExtensions.end()) { OCLExtensions.erase(Loc); OCLOptionalCoreFeatures.insert(I); } } addNamedMetadataStringSet(Context, M, kSPIR2MD::Extensions, OCLExtensions); addNamedMetadataStringSet(Context, M, kSPIR2MD::OptFeatures, OCLOptionalCoreFeatures); return true; } // If the argument is unsigned return uconvert*, otherwise return convert*. std::string SPIRVToLLVM::getOCLConvertBuiltinName(SPIRVInstruction* BI) { auto OC = BI->getOpCode(); assert(isCvtOpCode(OC) && "Not convert instruction"); auto U = static_cast(BI); std::string Name; if (isCvtFromUnsignedOpCode(OC)) Name = "u"; Name += "convert_"; Name += mapSPIRVTypeToOCLType(U->getType(), !isCvtToUnsignedOpCode(OC)); SPIRVFPRoundingModeKind Rounding; if (U->isSaturatedConversion()) Name += "_sat"; if (U->hasFPRoundingMode(&Rounding)) { Name += "_"; Name += SPIRSPIRVFPRoundingModeMap::rmap(Rounding); } return Name; } //Check Address Space of the Pointer Type std::string SPIRVToLLVM::getOCLGenericCastToPtrName(SPIRVInstruction* BI) { auto GenericCastToPtrInst = BI->getType()->getPointerStorageClass(); switch (GenericCastToPtrInst) { case StorageClassCrossWorkgroup: return std::string(kOCLBuiltinName::ToGlobal); case StorageClassWorkgroup: return std::string(kOCLBuiltinName::ToLocal); case StorageClassFunction: return std::string(kOCLBuiltinName::ToPrivate); default: llvm_unreachable("Invalid address space"); return ""; } } llvm::GlobalValue::LinkageTypes SPIRVToLLVM::transLinkageType(const SPIRVValue* V) { if (V->getLinkageType() == LinkageTypeInternal) { return GlobalValue::InternalLinkage; } else if (V->getLinkageType() == LinkageTypeImport) { // Function declaration if (V->getOpCode() == OpFunction) { if (static_cast(V)->getNumBasicBlock() == 0) return GlobalValue::ExternalLinkage; } // Variable declaration if (V->getOpCode() == OpVariable) { if (static_cast(V)->getInitializer() == 0) return GlobalValue::ExternalLinkage; } // Definition return GlobalValue::AvailableExternallyLinkage; } else {// LinkageTypeExport if (V->getOpCode() == OpVariable) { if (static_cast(V)->getInitializer() == 0 ) // Tentative definition return GlobalValue::CommonLinkage; } return GlobalValue::ExternalLinkage; } } Instruction *SPIRVToLLVM::transOCLAllAny(SPIRVInstruction *I, BasicBlock *BB) { CallInst *CI = cast(transSPIRVBuiltinFromInst(I, BB)); AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); return cast(mapValue( I, mutateCallInstOCL( M, CI, [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { Type *Int32Ty = Type::getInt32Ty(*Context); auto OldArg = CI->getOperand(0); auto NewArgTy = VectorType::get( Int32Ty, OldArg->getType()->getVectorNumElements()); auto NewArg = CastInst::CreateSExtOrBitCast(OldArg, NewArgTy, "", CI); Args[0] = NewArg; RetTy = Int32Ty; return CI->getCalledFunction()->getName(); }, [=](CallInst *NewCI) -> Instruction * { return CastInst::CreateTruncOrBitCast( NewCI, Type::getInt1Ty(*Context), "", NewCI->getNextNode()); }, &Attrs))); } Instruction *SPIRVToLLVM::transOCLRelational(SPIRVInstruction *I, BasicBlock *BB) { CallInst *CI = cast(transSPIRVBuiltinFromInst(I, BB)); AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); return cast(mapValue( I, mutateCallInstOCL( M, CI, [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { Type *IntTy = Type::getInt32Ty(*Context); RetTy = IntTy; if (CI->getType()->isVectorTy()) { if (cast(CI->getOperand(0)->getType()) ->getElementType() ->isDoubleTy()) IntTy = Type::getInt64Ty(*Context); if (cast(CI->getOperand(0)->getType()) ->getElementType() ->isHalfTy()) IntTy = Type::getInt16Ty(*Context); RetTy = VectorType::get(IntTy, CI->getType()->getVectorNumElements()); } return CI->getCalledFunction()->getName(); }, [=](CallInst *NewCI) -> Instruction * { Type *RetTy = Type::getInt1Ty(*Context); if (NewCI->getType()->isVectorTy()) RetTy = VectorType::get(Type::getInt1Ty(*Context), NewCI->getType()->getVectorNumElements()); return CastInst::CreateTruncOrBitCast(NewCI, RetTy, "", NewCI->getNextNode()); }, &Attrs))); } } bool llvm::ReadSPIRV(LLVMContext &C, std::istream &IS, Module *&M, std::string &ErrMsg) { M = new Module("", C); std::unique_ptr BM(SPIRVModule::createSPIRVModule()); IS >> *BM; SPIRVToLLVM BTL(M, BM.get()); bool Succeed = true; if (!BTL.translate()) { BM->getError(ErrMsg); Succeed = false; } legacy::PassManager PassMgr; PassMgr.add(createSPIRVToOCL20()); PassMgr.add(createOCL20To12()); PassMgr.run(*M); if (DbgSaveTmpLLVM) dumpLLVM(M, DbgTmpLLVMFileName); if (!Succeed) { delete M; M = nullptr; } return Succeed; }