//===- SPIRVWriter.cpp - Converts LLVM to SPIR-V ----------------*- 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 LLVM intermediate language to SPIR-V /// binary. /// //===----------------------------------------------------------------------===// #include "SPIRVModule.h" #include "SPIRVEnum.h" #include "SPIRVEntry.h" #include "SPIRVType.h" #include "SPIRVValue.h" #include "SPIRVFunction.h" #include "SPIRVBasicBlock.h" #include "SPIRVInstruction.h" #include "SPIRVExtInst.h" #include "SPIRVUtil.h" #include "SPIRVInternal.h" #include "SPIRVMDWalker.h" #include "OCLTypeToSPIRV.h" #include "OCLUtil.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Verifier.h" #include "llvm/Pass.h" #include "llvm/PassSupport.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Transforms/IPO.h" #include <iostream> #include <list> #include <memory> #include <set> #include <sstream> #include <vector> #include <functional> #include <cstdlib> #define DEBUG_TYPE "spirv" using namespace llvm; using namespace SPIRV; using namespace OCLUtil; namespace llvm { FunctionPass *createPromoteMemoryToRegisterPass(); } namespace SPIRV{ cl::opt<bool> SPIRVMemToReg("spirv-mem2reg", cl::init(true), cl::desc("LLVM/SPIR-V translation enable mem2reg")); static void foreachKernelArgMD(MDNode *MD, SPIRVFunction *BF, std::function<void(const std::string& Str, SPIRVFunctionParameter *BA)>Func) { for (unsigned I = 1, E = MD->getNumOperands(); I != E; ++I) { SPIRVFunctionParameter *BA = BF->getArgument(I-1); Func(getMDOperandAsString(MD, I), BA); } } /// Information for translating OCL builtin. struct OCLBuiltinSPIRVTransInfo { std::string UniqName; /// Postprocessor of operands std::function<void(std::vector<SPIRVWord>&)> PostProc; OCLBuiltinSPIRVTransInfo(){ PostProc = [](std::vector<SPIRVWord>&){}; } }; class LLVMToSPIRVDbgTran { public: LLVMToSPIRVDbgTran(Module *TM = nullptr, SPIRVModule *TBM = nullptr) :BM(TBM), M(TM){ } void setModule(Module *Mod) { M = Mod;} void setSPIRVModule(SPIRVModule *SMod) { BM = SMod;} void transDbgInfo(Value *V, SPIRVValue *BV) { if (auto I = dyn_cast<Instruction>(V)) { auto DL = I->getDebugLoc(); if (DL.get() != nullptr) { DILocation* DIL = DL.get(); auto File = BM->getString(DIL->getFilename().str()); // ToDo: SPIR-V rev.31 cannot add debug info for instructions without ids. // This limitation needs to be addressed. if (!BV->hasId()) return; BM->addLine(BV, File, DL.getLine(), DL.getCol()); } } else if (auto F = dyn_cast<Function>(V)) { if (auto DIS = F->getSubprogram()) { auto File = BM->getString(DIS->getFilename().str()); BM->addLine(BV, File, DIS->getLine(), 0); } } } private: SPIRVModule *BM; Module *M; }; class LLVMToSPIRV: public ModulePass { public: LLVMToSPIRV(SPIRVModule *SMod = nullptr) : ModulePass(ID), M(nullptr), Ctx(nullptr), BM(SMod), ExtSetId(SPIRVID_INVALID), SrcLang(0), SrcLangVer(0), DbgTran(nullptr, SMod){ } bool runOnModule(Module &Mod) override { M = &Mod; Ctx = &M->getContext(); DbgTran.setModule(M); assert(BM && "SPIR-V module not initialized"); translate(); return true; } void getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired<OCLTypeToSPIRV>(); } static char ID; SPIRVType *transType(Type *T); SPIRVType *transSPIRVOpaqueType(Type *T); SPIRVValue *getTranslatedValue(Value *); // Translation functions bool transAddressingMode(); bool transAlign(Value *V, SPIRVValue *BV); std::vector<SPIRVValue *> transArguments(CallInst *, SPIRVBasicBlock *); std::vector<SPIRVWord> transArguments(CallInst *, SPIRVBasicBlock *, SPIRVEntry *); bool transSourceLanguage(); bool transExtension(); bool transBuiltinSet(); SPIRVValue *transCallInst(CallInst *Call, SPIRVBasicBlock *BB); bool transDecoration(Value *V, SPIRVValue *BV); SPIRVWord transFunctionControlMask(CallInst *); SPIRVWord transFunctionControlMask(Function *); SPIRVFunction *transFunctionDecl(Function *F); bool transGlobalVariables(); Op transBoolOpCode(SPIRVValue *Opn, Op OC); // Translate LLVM module to SPIR-V module. // Returns true if succeeds. bool translate(); bool transExecutionMode(); SPIRVValue *transConstant(Value *V); SPIRVValue *transValue(Value *V, SPIRVBasicBlock *BB, bool CreateForward = true); SPIRVValue *transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB, bool CreateForward = true); typedef DenseMap<Type *, SPIRVType *> LLVMToSPIRVTypeMap; typedef DenseMap<Value *, SPIRVValue *> LLVMToSPIRVValueMap; private: Module *M; LLVMContext *Ctx; SPIRVModule *BM; LLVMToSPIRVTypeMap TypeMap; LLVMToSPIRVValueMap ValueMap; //ToDo: support multiple builtin sets. Currently assume one builtin set. SPIRVId ExtSetId; SPIRVWord SrcLang; SPIRVWord SrcLangVer; LLVMToSPIRVDbgTran DbgTran; SPIRVType *mapType(Type *T, SPIRVType *BT) { TypeMap[T] = BT; SPIRVDBG(dbgs() << "[mapType] " << *T << " => "; spvdbgs() << *BT << '\n'); return BT; } SPIRVValue *mapValue(Value *V, SPIRVValue *BV) { auto Loc = ValueMap.find(V); if (Loc != ValueMap.end()) { if (Loc->second == BV) return BV; assert (Loc->second->isForward() && "LLVM Value is mapped to different SPIRV Values"); auto Forward = static_cast<SPIRVForward *>(Loc->second); BV->setId(Forward->getId()); BM->replaceForward(Forward, BV); } ValueMap[V] = BV; SPIRVDBG(dbgs() << "[mapValue] " << *V << " => "; spvdbgs() << *BV << "\n"); return BV; } SPIRVType *getSPIRVType(Type *T) { return TypeMap[T]; } SPIRVValue *getSPIRVValue(Value *V) { return ValueMap[V]; } SPIRVErrorLog &getErrorLog() { return BM->getErrorLog(); } llvm::IntegerType* getSizetType(); std::vector<SPIRVValue*> transValue(const std::vector<Value *> &Values, SPIRVBasicBlock* BB); std::vector<SPIRVWord> transValue(const std::vector<Value *> &Values, SPIRVBasicBlock* BB, SPIRVEntry *Entry); SPIRVInstruction* transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB); SPIRVInstruction* transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB); void dumpUsers(Value *V); template<class ExtInstKind> bool oclGetExtInstIndex(const std::string &MangledName, const std::string& DemangledName, SPIRVWord* EntryPoint); void oclGetMutatedArgumentTypesByBuiltin(llvm::FunctionType* FT, std::map<unsigned, Type*>& ChangedType, Function* F); bool isBuiltinTransToInst(Function *F); bool isBuiltinTransToExtInst(Function *F, SPIRVExtInstSetKind *BuiltinSet = nullptr, SPIRVWord *EntryPoint = nullptr, SmallVectorImpl<std::string> *Dec = nullptr); bool oclIsKernel(Function *F); bool transOCLKernelMetadata(); SPIRVInstruction *transBuiltinToInst(const std::string& DemangledName, const std::string &MangledName, CallInst* CI, SPIRVBasicBlock* BB); SPIRVInstruction *transBuiltinToInstWithoutDecoration(Op OC, CallInst* CI, SPIRVBasicBlock* BB); void mutateFuncArgType(const std::map<unsigned, Type*>& ChangedType, Function* F); SPIRVValue *transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB); SPIRVValue *oclTransSpvcCastSampler(CallInst* CI, SPIRVBasicBlock *BB); SPIRV::SPIRVInstruction* transUnaryInst(UnaryInstruction* U, SPIRVBasicBlock* BB); /// Add a 32 bit integer constant. /// \return Id of the constant. SPIRVId addInt32(int); void transFunction(Function *I); SPIRV::SPIRVLinkageTypeKind transLinkageType(const GlobalValue* GV); }; SPIRVValue * LLVMToSPIRV::getTranslatedValue(Value *V) { LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V); if (Loc != ValueMap.end()) return Loc->second; return nullptr; } bool LLVMToSPIRV::oclIsKernel(Function *F) { if (F->getCallingConv() == CallingConv::SPIR_KERNEL) return true; return false; } bool LLVMToSPIRV::isBuiltinTransToInst(Function *F) { std::string DemangledName; if (!oclIsBuiltin(F->getName(), &DemangledName) && !isDecoratedSPIRVFunc(F, &DemangledName)) return false; SPIRVDBG(spvdbgs() << "CallInst: demangled name: " << DemangledName << '\n'); return getSPIRVFuncOC(DemangledName) != OpNop; } bool LLVMToSPIRV::isBuiltinTransToExtInst(Function *F, SPIRVExtInstSetKind *ExtSet, SPIRVWord *ExtOp, SmallVectorImpl<std::string> *Dec) { std::string OrigName = F->getName(); std::string DemangledName; if (!oclIsBuiltin(OrigName, &DemangledName)) return false; DEBUG(dbgs() << "[oclIsBuiltinTransToExtInst] CallInst: demangled name: " << DemangledName << '\n'); StringRef S = DemangledName; if (!S.startswith(kSPIRVName::Prefix)) return false; S = S.drop_front(strlen(kSPIRVName::Prefix)); auto Loc = S.find(kSPIRVPostfix::Divider); auto ExtSetName = S.substr(0, Loc); SPIRVExtInstSetKind Set = SPIRVEIS_Count; if (!SPIRVExtSetShortNameMap::rfind(ExtSetName, &Set)) return false; assert(Set == BM->getBuiltinSet(ExtSetId) && "Invalid extended instruction set"); assert(Set == SPIRVEIS_OpenCL && "Unsupported extended instruction set"); auto ExtOpName = S.substr(Loc + 1); auto Splited = ExtOpName.split(kSPIRVPostfix::ExtDivider); OCLExtOpKind EOC; if (!OCLExtOpMap::rfind(Splited.first, &EOC)) return false; if (ExtSet) *ExtSet = Set; if (ExtOp) *ExtOp = EOC; if (Dec) { SmallVector<StringRef, 2> P; Splited.second.split(P, kSPIRVPostfix::Divider); for (auto &I:P) Dec->push_back(I.str()); } return true; } /// Decode SPIR-V type name in the format spirv.{TypeName}._{Postfixes} /// where Postfixes are strings separated by underscores. /// \return TypeName. /// \param Ops contains the integers decoded from postfixes. static std::string decodeSPIRVTypeName(StringRef Name, SmallVectorImpl<std::string>& Strs) { SmallVector<StringRef, 4> SubStrs; const char Delim[] = { kSPIRVTypeName::Delimiter, 0 }; Name.split(SubStrs, Delim, -1, true); assert(SubStrs.size() >= 2 && "Invalid SPIRV type name"); assert(SubStrs[0] == kSPIRVTypeName::Prefix && "Invalid prefix"); assert((SubStrs.size() == 2 || !SubStrs[2].empty()) && "Invalid postfix"); if (SubStrs.size() > 2) { const char PostDelim[] = { kSPIRVTypeName::PostfixDelim, 0 }; SmallVector<StringRef, 4> Postfixes; SubStrs[2].split(Postfixes, PostDelim, -1, true); assert(Postfixes.size() > 1 && Postfixes[0].empty() && "Invalid postfix"); for (unsigned I = 1, E = Postfixes.size(); I != E; ++I) Strs.push_back(std::string(Postfixes[I]).c_str()); } return SubStrs[1].str(); } static bool recursiveType(const StructType *ST, const Type *Ty) { SmallPtrSet<const StructType *, 4> Seen; std::function<bool(const Type *Ty)> Run = [&](const Type *Ty) { if (!isa<CompositeType>(Ty)) return false; if (auto *StructTy = dyn_cast<StructType>(Ty)) { if (StructTy == ST) return true; if (Seen.count(StructTy)) return false; Seen.insert(StructTy); return find_if(StructTy->subtype_begin(), StructTy->subtype_end(), Run) != StructTy->subtype_end(); } if (auto *PtrTy = dyn_cast<PointerType>(Ty)) return Run(PtrTy->getPointerElementType()); if (auto *ArrayTy = dyn_cast<ArrayType>(Ty)) return Run(ArrayTy->getArrayElementType()); return false; }; return Run(Ty); } SPIRVType * LLVMToSPIRV::transType(Type *T) { LLVMToSPIRVTypeMap::iterator Loc = TypeMap.find(T); if (Loc != TypeMap.end()) return Loc->second; SPIRVDBG(dbgs() << "[transType] " << *T << '\n'); if (T->isVoidTy()) return mapType(T, BM->addVoidType()); if (T->isIntegerTy(1)) return mapType(T, BM->addBoolType()); if (T->isIntegerTy()) return mapType(T, BM->addIntegerType(T->getIntegerBitWidth())); if (T->isFloatingPointTy()) return mapType(T, BM->addFloatType(T->getPrimitiveSizeInBits())); // A pointer to image or pipe type in LLVM is translated to a SPIRV // sampler or pipe type. if (T->isPointerTy()) { auto ET = T->getPointerElementType(); assert(!ET->isFunctionTy() && "Function pointer type is not allowed"); auto ST = dyn_cast<StructType>(ET); auto AddrSpc = T->getPointerAddressSpace(); if (ST && !ST->isSized()) { Op OpCode; StringRef STName = ST->getName(); // Workaround for non-conformant SPIR binary if (STName == "struct._event_t") { STName = kSPR2TypeName::Event; ST->setName(STName); } assert (!STName.startswith(kSPR2TypeName::Pipe) && "OpenCL type names should be translated to SPIR-V type names"); // ToDo: For SPIR1.2/2.0 there may still be load/store or bitcast // instructions using opencl.* type names. We need to handle these // type names until they are all mapped or FE generates SPIR-V type // names. if (STName.find(kSPR2TypeName::Pipe) == 0) { assert(AddrSpc == SPIRAS_Global); SmallVector<StringRef, 4> SubStrs; const char Delims[] = {kSPR2TypeName::Delimiter, 0}; STName.split(SubStrs, Delims); std::string Acc = kAccessQualName::ReadOnly; if (SubStrs.size() > 2) { Acc = SubStrs[2]; } auto PipeT = BM->addPipeType(); PipeT->setPipeAcessQualifier(SPIRSPIRVAccessQualifierMap::map(Acc)); return mapType(T, PipeT); } else if (STName.find(kSPR2TypeName::ImagePrefix) == 0) { assert(AddrSpc == SPIRAS_Global); auto SPIRVImageTy = getSPIRVImageTypeFromOCL(M, T); return mapType(T, transSPIRVOpaqueType(SPIRVImageTy)); } else if (STName.startswith(kSPIRVTypeName::PrefixAndDelim)) return transSPIRVOpaqueType(T); else if (OCLOpaqueTypeOpCodeMap::find(STName, &OpCode)) { switch (OpCode) { default: return mapType(T, BM->addOpaqueGenericType(OpCode)); case OpTypePipe: return mapType(T, BM->addPipeType()); case OpTypeDeviceEvent: return mapType(T, BM->addDeviceEventType()); case OpTypeQueue: return mapType(T, BM->addQueueType()); } } else if (isPointerToOpaqueStructType(T)) { return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map( static_cast<SPIRAddressSpace>(AddrSpc)), transType(ET))); } } else { return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map( static_cast<SPIRAddressSpace>(AddrSpc)), transType(ET))); } } if (T->isVectorTy()) return mapType(T, BM->addVectorType(transType(T->getVectorElementType()), T->getVectorNumElements())); if (T->isArrayTy()) return mapType(T, BM->addArrayType(transType(T->getArrayElementType()), static_cast<SPIRVConstant*>(transValue(ConstantInt::get(getSizetType(), T->getArrayNumElements(), false), nullptr)))); if (T->isStructTy() && !T->isSized()) { auto ST = dyn_cast<StructType>(T); (void) ST; assert(!ST->getName().startswith(kSPR2TypeName::Pipe)); assert(!ST->getName().startswith(kSPR2TypeName::ImagePrefix)); return mapType(T, BM->addOpaqueType(T->getStructName())); } if (auto ST = dyn_cast<StructType>(T)) { assert(ST->isSized()); std::string Name; if (ST->hasName()) Name = ST->getName(); if(Name == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) return transType(getSamplerType(M)); if (Name == getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage)) return transType(getPipeStorageType(M)); auto *Struct = BM->openStructType(T->getStructNumElements(), Name); mapType(T, Struct); SmallVector<unsigned, 4> ForwardRefs; for (unsigned I = 0, E = T->getStructNumElements(); I != E; ++I) { auto *ElemTy = ST->getElementType(I); if (isa<CompositeType>(ElemTy) && recursiveType(ST, ElemTy)) ForwardRefs.push_back(I); else Struct->setMemberType(I, transType(ST->getElementType(I))); } BM->closeStructType(Struct, ST->isPacked()); for (auto I : ForwardRefs) Struct->setMemberType(I, transType(ST->getElementType(I))); return Struct; } if (FunctionType *FT = dyn_cast<FunctionType>(T)) { SPIRVType *RT = transType(FT->getReturnType()); std::vector<SPIRVType *> PT; for (FunctionType::param_iterator I = FT->param_begin(), E = FT->param_end(); I != E; ++I) PT.push_back(transType(*I)); return mapType(T, BM->addFunctionType(RT, PT)); } llvm_unreachable("Not implemented!"); return 0; } SPIRVType * LLVMToSPIRV::transSPIRVOpaqueType(Type *T) { auto ET = T->getPointerElementType(); auto ST = cast<StructType>(ET); auto AddrSpc = T->getPointerAddressSpace(); (void)AddrSpc; // prevent warning about unused variable in NDEBUG build auto STName = ST->getStructName(); assert (STName.startswith(kSPIRVTypeName::PrefixAndDelim) && "Invalid SPIR-V opaque type name"); SmallVector<std::string, 8> Postfixes; auto TN = decodeSPIRVTypeName(STName, Postfixes); if (TN == kSPIRVTypeName::Pipe) { assert(AddrSpc == SPIRAS_Global); assert(Postfixes.size() == 1 && "Invalid pipe type ops"); auto PipeT = BM->addPipeType(); PipeT->setPipeAcessQualifier(static_cast<spv::AccessQualifier>( atoi(Postfixes[0].c_str()))); return mapType(T, PipeT); } else if (TN == kSPIRVTypeName::Image) { assert(AddrSpc == SPIRAS_Global); // The sampled type needs to be translated through LLVM type to guarantee // uniqueness. auto SampledT = transType(getLLVMTypeForSPIRVImageSampledTypePostfix( Postfixes[0], *Ctx)); SmallVector<int, 7> Ops; for (unsigned I = 1; I < 8; ++I) Ops.push_back(atoi(Postfixes[I].c_str())); SPIRVTypeImageDescriptor Desc(static_cast<SPIRVImageDimKind>(Ops[0]), Ops[1], Ops[2], Ops[3], Ops[4], Ops[5]); return mapType(T, BM->addImageType(SampledT, Desc, static_cast<spv::AccessQualifier>(Ops[6]))); } else if (TN == kSPIRVTypeName::SampledImg) { return mapType(T, BM->addSampledImageType( static_cast<SPIRVTypeImage *>( transType(getSPIRVTypeByChangeBaseTypeName(M, T, kSPIRVTypeName::SampledImg, kSPIRVTypeName::Image))))); } else if(TN == kSPIRVTypeName::Sampler) return mapType(T, BM->addSamplerType()); else if (TN == kSPIRVTypeName::DeviceEvent) return mapType(T, BM->addDeviceEventType()); else if (TN == kSPIRVTypeName::Queue) return mapType(T, BM->addQueueType()); else if (TN == kSPIRVTypeName::PipeStorage) return mapType(T, BM->addPipeStorageType()); else return mapType(T, BM->addOpaqueGenericType( SPIRVOpaqueTypeOpCodeMap::map(TN))); } SPIRVFunction * LLVMToSPIRV::transFunctionDecl(Function *F) { if (auto BF= getTranslatedValue(F)) return static_cast<SPIRVFunction *>(BF); SPIRVTypeFunction *BFT = static_cast<SPIRVTypeFunction *>(transType( getAnalysis<OCLTypeToSPIRV>().getAdaptedType(F))); SPIRVFunction *BF = static_cast<SPIRVFunction *>(mapValue(F, BM->addFunction(BFT))); BF->setFunctionControlMask(transFunctionControlMask(F)); if (F->hasName()) BM->setName(BF, F->getName()); if (oclIsKernel(F)) BM->addEntryPoint(ExecutionModelKernel, BF->getId()); else if (F->getLinkage() != GlobalValue::InternalLinkage) BF->setLinkageType(transLinkageType(F)); auto Attrs = F->getAttributes(); for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I) { auto ArgNo = I->getArgNo(); SPIRVFunctionParameter *BA = BF->getArgument(ArgNo); if (I->hasName()) BM->setName(BA, I->getName()); if (I->hasByValAttr()) BA->addAttr(FunctionParameterAttributeByVal); if (I->hasNoAliasAttr()) BA->addAttr(FunctionParameterAttributeNoAlias); if (I->hasNoCaptureAttr()) BA->addAttr(FunctionParameterAttributeNoCapture); if (I->hasStructRetAttr()) BA->addAttr(FunctionParameterAttributeSret); if (Attrs.hasAttribute(ArgNo + 1, Attribute::ZExt)) BA->addAttr(FunctionParameterAttributeZext); if (Attrs.hasAttribute(ArgNo + 1, Attribute::SExt)) BA->addAttr(FunctionParameterAttributeSext); if (Attrs.hasAttribute(ArgNo + 1, Attribute::Dereferenceable)) BA->addDecorate(DecorationMaxByteOffset, Attrs.getAttribute(ArgNo + 1, Attribute::Dereferenceable) .getDereferenceableBytes()); } if (Attrs.hasAttribute(AttributeSet::ReturnIndex, Attribute::ZExt)) BF->addDecorate(DecorationFuncParamAttr, FunctionParameterAttributeZext); if (Attrs.hasAttribute(AttributeSet::ReturnIndex, Attribute::SExt)) BF->addDecorate(DecorationFuncParamAttr, FunctionParameterAttributeSext); DbgTran.transDbgInfo(F, BF); SPIRVDBG(dbgs() << "[transFunction] " << *F << " => "; spvdbgs() << *BF << '\n';) return BF; } #define _SPIRV_OPL(x) OpLogical##x #define _SPIRV_OPB(x) OpBitwise##x SPIRVValue * LLVMToSPIRV::transConstant(Value *V) { if (auto CPNull = dyn_cast<ConstantPointerNull>(V)) return BM->addNullConstant(bcast<SPIRVTypePointer>(transType( CPNull->getType()))); if (auto CAZero = dyn_cast<ConstantAggregateZero>(V)) { Type *AggType = CAZero->getType(); if (const StructType* ST = dyn_cast<StructType>(AggType)) if (ST->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) return BM->addSamplerConstant(transType(AggType), 0,0,0); return BM->addNullConstant(transType(AggType)); } if (auto ConstI = dyn_cast<ConstantInt>(V)) return BM->addConstant(transType(V->getType()), ConstI->getZExtValue()); if (auto ConstFP = dyn_cast<ConstantFP>(V)) { auto BT = static_cast<SPIRVType *>(transType(V->getType())); return BM->addConstant(BT, ConstFP->getValueAPF().bitcastToAPInt().getZExtValue()); } if (auto ConstDA = dyn_cast<ConstantDataArray>(V)) { std::vector<SPIRVValue *> BV; for (unsigned I = 0, E = ConstDA->getNumElements(); I != E; ++I) BV.push_back(transValue(ConstDA->getElementAsConstant(I), nullptr)); return BM->addCompositeConstant(transType(V->getType()), BV); } if (auto ConstA = dyn_cast<ConstantArray>(V)) { std::vector<SPIRVValue *> BV; for (auto I = ConstA->op_begin(), E = ConstA->op_end(); I != E; ++I) BV.push_back(transValue(*I, nullptr)); return BM->addCompositeConstant(transType(V->getType()), BV); } if (auto ConstDV = dyn_cast<ConstantDataVector>(V)) { std::vector<SPIRVValue *> BV; for (unsigned I = 0, E = ConstDV->getNumElements(); I != E; ++I) BV.push_back(transValue(ConstDV->getElementAsConstant(I), nullptr)); return BM->addCompositeConstant(transType(V->getType()), BV); } if (auto ConstV = dyn_cast<ConstantVector>(V)) { std::vector<SPIRVValue *> BV; for (auto I = ConstV->op_begin(), E = ConstV->op_end(); I != E; ++I) BV.push_back(transValue(*I, nullptr)); return BM->addCompositeConstant(transType(V->getType()), BV); } if (auto ConstV = dyn_cast<ConstantStruct>(V)) { if (ConstV->getType()->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) { assert(ConstV->getNumOperands() == 3); SPIRVWord AddrMode = ConstV->getOperand(0)->getUniqueInteger().getZExtValue(), Normalized = ConstV->getOperand(1)->getUniqueInteger().getZExtValue(), FilterMode = ConstV->getOperand(2)->getUniqueInteger().getZExtValue(); assert(AddrMode < 5 && "Invalid addressing mode"); assert(Normalized < 2 && "Invalid value of normalized coords"); assert(FilterMode < 2 && "Invalid filter mode"); SPIRVType* SamplerTy = transType(ConstV->getType()); return BM->addSamplerConstant(SamplerTy, AddrMode, Normalized, FilterMode); } if (ConstV->getType()->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage)) { assert(ConstV->getNumOperands() == 3); SPIRVWord PacketSize = ConstV->getOperand(0)->getUniqueInteger().getZExtValue(), PacketAlign = ConstV->getOperand(1)->getUniqueInteger().getZExtValue(), Capacity = ConstV->getOperand(2)->getUniqueInteger().getZExtValue(); assert(PacketAlign >= 1 && "Invalid packet alignment"); assert(PacketSize >= PacketAlign && PacketSize % PacketAlign == 0 && "Invalid packet size and/or alignment."); SPIRVType* PipeStorageTy = transType(ConstV->getType()); return BM->addPipeStorageConstant(PipeStorageTy, PacketSize, PacketAlign, Capacity); } std::vector<SPIRVValue *> BV; for (auto I = ConstV->op_begin(), E = ConstV->op_end(); I != E; ++I) BV.push_back(transValue(*I, nullptr)); return BM->addCompositeConstant(transType(V->getType()), BV); } if (auto ConstUE = dyn_cast<ConstantExpr>(V)) { auto Inst = ConstUE->getAsInstruction(); SPIRVDBG(dbgs() << "ConstantExpr: " << *ConstUE << '\n'; dbgs() << "Instruction: " << *Inst << '\n';) auto BI = transValue(Inst, nullptr, false); Inst->dropAllReferences(); return BI; } if (isa<UndefValue>(V)) { return BM->addUndef(transType(V->getType())); } return nullptr; } SPIRVValue * LLVMToSPIRV::transValue(Value *V, SPIRVBasicBlock *BB, bool CreateForward) { LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V); if (Loc != ValueMap.end() && (!Loc->second->isForward() || CreateForward)) return Loc->second; SPIRVDBG(dbgs() << "[transValue] " << *V << '\n'); assert ((!isa<Instruction>(V) || isa<GetElementPtrInst>(V) || isa<CastInst>(V) || BB) && "Invalid SPIRV BB"); auto BV = transValueWithoutDecoration(V, BB, CreateForward); std::string name = V->getName(); if (!name.empty()) // Don't erase the name, which BM might already have BM->setName(BV, name); if(!transDecoration(V, BV)) return nullptr; return BV; } SPIRVInstruction* LLVMToSPIRV::transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB) { unsigned LLVMOC = B->getOpcode(); auto Op0 = transValue(B->getOperand(0), BB); SPIRVInstruction* BI = BM->addBinaryInst( transBoolOpCode(Op0, OpCodeMap::map(LLVMOC)), transType(B->getType()), Op0, transValue(B->getOperand(1), BB), BB); return BI; } SPIRVInstruction* LLVMToSPIRV::transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB) { auto Op0 = transValue(Cmp->getOperand(0), BB); SPIRVInstruction* BI = BM->addCmpInst( transBoolOpCode(Op0, CmpMap::map(Cmp->getPredicate())), transType(Cmp->getType()), Op0, transValue(Cmp->getOperand(1), BB), BB); return BI; } SPIRV::SPIRVInstruction *LLVMToSPIRV::transUnaryInst(UnaryInstruction *U, SPIRVBasicBlock *BB) { Op BOC = OpNop; if (auto Cast = dyn_cast<AddrSpaceCastInst>(U)) { if (Cast->getDestTy()->getPointerAddressSpace() == SPIRAS_Generic) { assert(Cast->getSrcTy()->getPointerAddressSpace() != SPIRAS_Constant && "Casts from constant address space to generic are illegal"); BOC = OpPtrCastToGeneric; } else { assert(Cast->getDestTy()->getPointerAddressSpace() != SPIRAS_Constant && "Casts from generic address space to constant are illegal"); assert(Cast->getSrcTy()->getPointerAddressSpace() == SPIRAS_Generic); BOC = OpGenericCastToPtr; } } else { auto OpCode = U->getOpcode(); BOC = OpCodeMap::map(OpCode); } auto Op = transValue(U->getOperand(0), BB); return BM->addUnaryInst(transBoolOpCode(Op, BOC), transType(U->getType()), Op, BB); } /// An instruction may use an instruction from another BB which has not been /// translated. SPIRVForward should be created as place holder for these /// instructions and replaced later by the real instructions. /// Use CreateForward = true to indicate such situation. SPIRVValue * LLVMToSPIRV::transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB, bool CreateForward) { if (auto LBB = dyn_cast<BasicBlock>(V)) { auto BF = static_cast<SPIRVFunction *>(getTranslatedValue(LBB->getParent())); assert (BF && "Function not translated"); BB = static_cast<SPIRVBasicBlock *>(mapValue(V, BM->addBasicBlock(BF))); BM->setName(BB, LBB->getName()); return BB; } if (auto F = dyn_cast<Function>(V)) return transFunctionDecl(F); if (auto GV = dyn_cast<GlobalVariable>(V)) { llvm::PointerType * Ty = GV->getType(); // Though variables with common linkage type are initialized by 0, // they can be represented in SPIR-V as uninitialized variables with // 'Export' linkage type, just as tentative definitions look in C llvm::Value *Init = GV->hasInitializer() && !GV->hasCommonLinkage() ? GV->getInitializer() : nullptr; StructType *ST = Init ? dyn_cast<StructType>(Init->getType()) : nullptr; if (ST && ST->hasName() && isSPIRVConstantName(ST->getName())) { auto BV = transConstant(Init); assert(BV); return mapValue(V, BV); } else if (ConstantExpr *ConstUE = dyn_cast_or_null<ConstantExpr>(Init)) { Instruction * Inst = ConstUE->getAsInstruction(); if (isSpecialTypeInitializer(Inst)) { Init = Inst->getOperand(0); Ty = static_cast<PointerType*>(Init->getType()); } Inst->dropAllReferences(); } auto BVar = static_cast<SPIRVVariable *>(BM->addVariable( transType(Ty), GV->isConstant(), transLinkageType(GV), Init ? transValue(Init, nullptr) : nullptr, GV->getName(), SPIRSPIRVAddrSpaceMap::map( static_cast<SPIRAddressSpace>(Ty->getAddressSpace())), nullptr )); mapValue(V, BVar); spv::BuiltIn Builtin = spv::BuiltInPosition; if (!GV->hasName() || !getSPIRVBuiltin(GV->getName().str(), Builtin)) return BVar; BVar->setBuiltin(Builtin); return BVar; } if (isa<Constant>(V)) { auto BV = transConstant(V); assert(BV); return mapValue(V, BV); } if (auto Arg = dyn_cast<Argument>(V)) { unsigned ArgNo = Arg->getArgNo(); SPIRVFunction *BF = BB->getParent(); //assert(BF->existArgument(ArgNo)); return mapValue(V, BF->getArgument(ArgNo)); } if (CreateForward) return mapValue(V, BM->addForward(transType(V->getType()))); if (StoreInst *ST = dyn_cast<StoreInst>(V)) { std::vector<SPIRVWord> MemoryAccess(1,0); if (ST->isVolatile()) MemoryAccess[0] |= MemoryAccessVolatileMask; if (ST->getAlignment()) { MemoryAccess[0] |= MemoryAccessAlignedMask; MemoryAccess.push_back(ST->getAlignment()); } if (ST->getMetadata(LLVMContext::MD_nontemporal)) MemoryAccess[0] |= MemoryAccessNontemporalMask; if (MemoryAccess.front() == 0) MemoryAccess.clear(); return mapValue(V, BM->addStoreInst( transValue(ST->getPointerOperand(), BB), transValue(ST->getValueOperand(), BB), MemoryAccess, BB)); } if (LoadInst *LD = dyn_cast<LoadInst>(V)) { std::vector<SPIRVWord> MemoryAccess(1,0); if (LD->isVolatile()) MemoryAccess[0] |= MemoryAccessVolatileMask; if (LD->getAlignment()) { MemoryAccess[0] |= MemoryAccessAlignedMask; MemoryAccess.push_back(LD->getAlignment()); } if (LD->getMetadata(LLVMContext::MD_nontemporal)) MemoryAccess[0] |= MemoryAccessNontemporalMask; if (MemoryAccess.front() == 0) MemoryAccess.clear(); return mapValue(V, BM->addLoadInst( transValue(LD->getPointerOperand(), BB), MemoryAccess, BB)); } if (BinaryOperator *B = dyn_cast<BinaryOperator>(V)) { SPIRVInstruction* BI = transBinaryInst(B, BB); return mapValue(V, BI); } if (auto RI = dyn_cast<ReturnInst>(V)) { if (auto RV = RI->getReturnValue()) return mapValue(V, BM->addReturnValueInst( transValue(RV, BB), BB)); return mapValue(V, BM->addReturnInst(BB)); } if (CmpInst *Cmp = dyn_cast<CmpInst>(V)) { SPIRVInstruction* BI = transCmpInst(Cmp, BB); return mapValue(V, BI); } if (SelectInst *Sel = dyn_cast<SelectInst>(V)) return mapValue(V, BM->addSelectInst( transValue(Sel->getCondition(), BB), transValue(Sel->getTrueValue(), BB), transValue(Sel->getFalseValue(), BB),BB)); if (AllocaInst *Alc = dyn_cast<AllocaInst>(V)) return mapValue(V, BM->addVariable( transType(Alc->getType()), false, SPIRVLinkageTypeKind::LinkageTypeInternal, nullptr, Alc->getName(), StorageClassFunction, BB)); if (auto *Switch = dyn_cast<SwitchInst>(V)) { std::vector<std::pair<SPIRVWord, SPIRVBasicBlock *>> Pairs; for (auto I = Switch->case_begin(), E = Switch->case_end(); I != E; ++I) Pairs.push_back(std::make_pair(I.getCaseValue()->getZExtValue(), static_cast<SPIRVBasicBlock*>(transValue(I.getCaseSuccessor(), nullptr)))); return mapValue(V, BM->addSwitchInst( transValue(Switch->getCondition(), BB), static_cast<SPIRVBasicBlock*>(transValue(Switch->getDefaultDest(), nullptr)), Pairs, BB)); } if (auto Branch = dyn_cast<BranchInst>(V)) { if (Branch->isUnconditional()) return mapValue(V, BM->addBranchInst( static_cast<SPIRVLabel*>(transValue(Branch->getSuccessor(0), BB)), BB)); return mapValue(V, BM->addBranchConditionalInst( transValue(Branch->getCondition(), BB), static_cast<SPIRVLabel*>(transValue(Branch->getSuccessor(0), BB)), static_cast<SPIRVLabel*>(transValue(Branch->getSuccessor(1), BB)), BB)); } if (auto Phi = dyn_cast<PHINode>(V)) { std::vector<SPIRVValue *> IncomingPairs; for (size_t I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { IncomingPairs.push_back(transValue(Phi->getIncomingValue(I), BB)); IncomingPairs.push_back(transValue(Phi->getIncomingBlock(I), nullptr)); } return mapValue(V, BM->addPhiInst(transType(Phi->getType()), IncomingPairs, BB)); } if (auto Ext = dyn_cast<ExtractValueInst>(V)) { return mapValue(V, BM->addCompositeExtractInst( transType(Ext->getType()), transValue(Ext->getAggregateOperand(), BB), Ext->getIndices(), BB)); } if (auto Ins = dyn_cast<InsertValueInst>(V)) { return mapValue(V, BM->addCompositeInsertInst( transValue(Ins->getInsertedValueOperand(), BB), transValue(Ins->getAggregateOperand(), BB), Ins->getIndices(), BB)); } if (UnaryInstruction *U = dyn_cast<UnaryInstruction>(V)) { if (isSpecialTypeInitializer(U)) return mapValue(V, transValue(U->getOperand(0), BB)); return mapValue(V, transUnaryInst(U, BB)); } if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V)) { std::vector<SPIRVValue *> Indices; for (unsigned i = 0, e = GEP->getNumIndices(); i != e; ++i) Indices.push_back(transValue(GEP->getOperand(i+1), BB)); return mapValue(V, BM->addPtrAccessChainInst( transType(GEP->getType()), transValue(GEP->getPointerOperand(), BB), Indices, BB, GEP->isInBounds())); } if (auto Ext = dyn_cast<ExtractElementInst>(V)) { auto Index = Ext->getIndexOperand(); if (auto Const = dyn_cast<ConstantInt>(Index)) return mapValue(V, BM->addCompositeExtractInst( transType(Ext->getType()), transValue(Ext->getVectorOperand(), BB), std::vector<SPIRVWord>(1, Const->getZExtValue()), BB)); else return mapValue(V, BM->addVectorExtractDynamicInst( transValue(Ext->getVectorOperand(), BB), transValue(Index, BB), BB)); } if (auto Ins = dyn_cast<InsertElementInst>(V)) { auto Index = Ins->getOperand(2); if (auto Const = dyn_cast<ConstantInt>(Index)) return mapValue(V, BM->addCompositeInsertInst( transValue(Ins->getOperand(1), BB), transValue(Ins->getOperand(0), BB), std::vector<SPIRVWord>(1, Const->getZExtValue()), BB)); else return mapValue(V, BM->addVectorInsertDynamicInst( transValue(Ins->getOperand(0), BB), transValue(Ins->getOperand(1), BB), transValue(Index, BB), BB)); } if (auto SF = dyn_cast<ShuffleVectorInst>(V)) { std::vector<SPIRVWord> Comp; for (auto &I:SF->getShuffleMask()) Comp.push_back(I); return mapValue(V, BM->addVectorShuffleInst( transType(SF->getType()), transValue(SF->getOperand(0), BB), transValue(SF->getOperand(1), BB), Comp, BB)); } if (CallInst *CI = dyn_cast<CallInst>(V)) return mapValue(V, transCallInst(CI, BB)); llvm_unreachable("Not implemented"); return nullptr; } bool LLVMToSPIRV::transDecoration(Value *V, SPIRVValue *BV) { if (!transAlign(V, BV)) return false; if ((isa<AtomicCmpXchgInst>(V) && cast<AtomicCmpXchgInst>(V)->isVolatile()) || (isa<AtomicRMWInst>(V) && cast<AtomicRMWInst>(V)->isVolatile())) BV->setVolatile(true); DbgTran.transDbgInfo(V, BV); return true; } bool LLVMToSPIRV::transAlign(Value *V, SPIRVValue *BV) { if (auto AL = dyn_cast<AllocaInst>(V)) { BM->setAlignment(BV, AL->getAlignment()); return true; } if (auto GV = dyn_cast<GlobalVariable>(V)) { BM->setAlignment(BV, GV->getAlignment()); return true; } return true; } /// Do this after source language is set. bool LLVMToSPIRV::transBuiltinSet() { SPIRVWord Ver = 0; SourceLanguage Kind = BM->getSourceLanguage(&Ver); (void) Kind; assert((Kind == SourceLanguageOpenCL_C || Kind == SourceLanguageOpenCL_CPP ) && "not supported"); std::stringstream SS; SS << "OpenCL.std"; return BM->importBuiltinSet(SS.str(), &ExtSetId); } /// Transform sampler* spcv.cast(i32 arg) /// Only two cases are possible: /// arg = ConstantInt x -> SPIRVConstantSampler /// arg = i32 argument -> transValue(arg) /// arg = load from sampler -> look through load SPIRVValue * LLVMToSPIRV::oclTransSpvcCastSampler(CallInst* CI, SPIRVBasicBlock *BB) { llvm::Function* F = CI->getCalledFunction(); auto FT = F->getFunctionType(); auto RT = FT->getReturnType(); assert(FT->getNumParams() == 1); assert(isSPIRVType(RT, kSPIRVTypeName::Sampler) && FT->getParamType(0)->isIntegerTy() && "Invalid sampler type"); auto Arg = CI->getArgOperand(0); auto GetSamplerConstant = [&](uint64_t SamplerValue) { auto AddrMode = (SamplerValue & 0xE) >> 1; auto Param = SamplerValue & 0x1; auto Filter = ((SamplerValue & 0x30) >> 4) - 1; auto BV = BM->addSamplerConstant(transType(RT), AddrMode, Param, Filter); return BV; }; if (auto Const = dyn_cast<ConstantInt>(Arg)) { // Sampler is declared as a kernel scope constant return GetSamplerConstant(Const->getZExtValue()); } else if (auto Load = dyn_cast<LoadInst>(Arg)) { // If value of the sampler is loaded from a global constant, use its // initializer for initialization of the sampler. auto Op = Load->getPointerOperand(); assert(isa<GlobalVariable>(Op) && "Unknown sampler pattern!"); auto GV = cast<GlobalVariable>(Op); assert(GV->isConstant() || GV->getType()->getPointerAddressSpace() == SPIRAS_Constant); auto Initializer = GV->getInitializer(); assert(isa<ConstantInt>(Initializer) && "sampler not constant int?"); return GetSamplerConstant(cast<ConstantInt>(Initializer)->getZExtValue()); } // Sampler is a function argument auto BV = transValue(Arg, BB); assert(BV && BV->getType() == transType(RT)); return BV; } SPIRVValue * LLVMToSPIRV::transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB) { return oclTransSpvcCastSampler(CI, BB); } SPIRVValue * LLVMToSPIRV::transCallInst(CallInst *CI, SPIRVBasicBlock *BB) { SPIRVExtInstSetKind ExtSetKind = SPIRVEIS_Count; SPIRVWord ExtOp = SPIRVWORD_MAX; llvm::Function* F = CI->getCalledFunction(); auto MangledName = F->getName(); std::string DemangledName; if (MangledName.startswith(SPCV_CAST)) return transSpcvCast(CI, BB); if (MangledName.startswith("llvm.memcpy")) { std::vector<SPIRVWord> MemoryAccess; if (isa<ConstantInt>(CI->getOperand(4)) && dyn_cast<ConstantInt>(CI->getOperand(4)) ->getZExtValue() == 1) MemoryAccess.push_back(MemoryAccessVolatileMask); if (isa<ConstantInt>(CI->getOperand(3))) { MemoryAccess.push_back(MemoryAccessAlignedMask); MemoryAccess.push_back(dyn_cast<ConstantInt>(CI->getOperand(3)) ->getZExtValue()); } return BM->addCopyMemorySizedInst( transValue(CI->getOperand(0), BB), transValue(CI->getOperand(1), BB), transValue(CI->getOperand(2), BB), MemoryAccess, BB); } if (oclIsBuiltin(MangledName, &DemangledName) || isDecoratedSPIRVFunc(F, &DemangledName)) if (auto BV = transBuiltinToInst(DemangledName, MangledName, CI, BB)) return BV; SmallVector<std::string, 2> Dec; if (isBuiltinTransToExtInst(CI->getCalledFunction(), &ExtSetKind, &ExtOp, &Dec)) return addDecorations(BM->addExtInst( transType(CI->getType()), ExtSetId, ExtOp, transArguments(CI, BB, SPIRVEntry::create_unique(ExtSetKind, ExtOp).get()), BB), Dec); return BM->addCallInst( transFunctionDecl(CI->getCalledFunction()), transArguments(CI, BB, SPIRVEntry::create_unique(OpFunctionCall).get()), BB); } bool LLVMToSPIRV::transAddressingMode() { Triple TargetTriple(M->getTargetTriple()); Triple::ArchType Arch = TargetTriple.getArch(); SPIRVCKRT(Arch == Triple::spir || Arch == Triple::spir64, InvalidTargetTriple, "Actual target triple is " + M->getTargetTriple()); if (Arch == Triple::spir) BM->setAddressingModel(AddressingModelPhysical32); else BM->setAddressingModel(AddressingModelPhysical64); // Physical addressing model requires Addresses capability BM->addCapability(CapabilityAddresses); return true; } std::vector<SPIRVValue*> LLVMToSPIRV::transValue(const std::vector<Value *> &Args, SPIRVBasicBlock* BB) { std::vector<SPIRVValue*> BArgs; for (auto &I: Args) BArgs.push_back(transValue(I, BB)); return BArgs; } std::vector<SPIRVValue*> LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB) { return transValue(getArguments(CI), BB); } std::vector<SPIRVWord> LLVMToSPIRV::transValue(const std::vector<Value *> &Args, SPIRVBasicBlock* BB, SPIRVEntry *Entry) { std::vector<SPIRVWord> Operands; for (size_t I = 0, E = Args.size(); I != E; ++I) { Operands.push_back(Entry->isOperandLiteral(I) ? cast<ConstantInt>(Args[I])->getZExtValue() : transValue(Args[I], BB)->getId()); } return Operands; } std::vector<SPIRVWord> LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB, SPIRVEntry *Entry) { return transValue(getArguments(CI), BB, Entry); } SPIRVWord LLVMToSPIRV::transFunctionControlMask(CallInst *CI) { SPIRVWord FCM = 0; SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, SPIRVFunctionControlMaskKind Mask){ if (CI->hasFnAttr(Attr)) FCM |= Mask; }); return FCM; } SPIRVWord LLVMToSPIRV::transFunctionControlMask(Function *F) { SPIRVWord FCM = 0; SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, SPIRVFunctionControlMaskKind Mask){ if (F->hasFnAttribute(Attr)) FCM |= Mask; }); return FCM; } bool LLVMToSPIRV::transGlobalVariables() { for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) { if (!transValue(static_cast<GlobalVariable*>(I), nullptr)) return false; } return true; } void LLVMToSPIRV::mutateFuncArgType(const std::map<unsigned, Type*>& ChangedType, Function* F) { for (auto &I : ChangedType) { for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE; ++UI) { auto Call = dyn_cast<CallInst>(*UI); if (!Call) continue; auto Arg = Call->getArgOperand(I.first); auto OrigTy = Arg->getType(); if (OrigTy == I.second) continue; SPIRVDBG(dbgs() << "[mutate arg type] " << *Call << ", " << *Arg << '\n'); auto CastF = M->getOrInsertFunction(SPCV_CAST, I.second, OrigTy, nullptr); std::vector<Value *> Args; Args.push_back(Arg); auto Cast = CallInst::Create(CastF, Args, "", Call); Call->replaceUsesOfWith(Arg, Cast); SPIRVDBG(dbgs() << "[mutate arg type] -> " << *Cast << '\n'); } } } void LLVMToSPIRV::transFunction(Function *I) { transFunctionDecl(I); // Creating all basic blocks before creating any instruction. for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) { transValue(static_cast<BasicBlock*>(FI), nullptr); } for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) { SPIRVBasicBlock* BB = static_cast<SPIRVBasicBlock*>(transValue(static_cast<BasicBlock*>(FI), nullptr)); for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI) { transValue(static_cast<Instruction*>(BI), BB, false); } } } bool LLVMToSPIRV::translate() { BM->setGeneratorVer(kTranslatorVer); if (!transSourceLanguage()) return false; if (!transExtension()) return false; if (!transBuiltinSet()) return false; if (!transAddressingMode()) return false; if (!transGlobalVariables()) return false; for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { Function *F = static_cast<Function*>(I); auto FT = F->getFunctionType(); std::map<unsigned, Type *> ChangedType; oclGetMutatedArgumentTypesByBuiltin(FT, ChangedType, F); mutateFuncArgType(ChangedType, F); } // SPIR-V logical layout requires all function declarations go before // function definitions. std::vector<Function *> Decls, Defs; for (Module::iterator I1 = M->begin(), E = M->end(); I1 != E; ++I1) { auto I = static_cast<Function*>(I1); if (isBuiltinTransToInst(I) || isBuiltinTransToExtInst(I) || I->getName().startswith(SPCV_CAST) || I->getName().startswith(LLVM_MEMCPY)) continue; if (I->isDeclaration()) Decls.push_back(I); else Defs.push_back(I); } for (auto I:Decls) transFunctionDecl(I); for (auto I:Defs) transFunction(I); if (!transOCLKernelMetadata()) return false; if (!transExecutionMode()) return false; BM->optimizeDecorates(); BM->resolveUnknownStructFields(); BM->createForwardPointers(); return true; } llvm::IntegerType* LLVMToSPIRV::getSizetType() { return IntegerType::getIntNTy(M->getContext(), M->getDataLayout().getPointerSizeInBits()); } void LLVMToSPIRV::oclGetMutatedArgumentTypesByBuiltin( llvm::FunctionType* FT, std::map<unsigned, Type*>& ChangedType, Function* F) { auto Name = F->getName(); std::string Demangled; if (!oclIsBuiltin(Name, &Demangled)) return; if (Demangled.find(kSPIRVName::SampledImage) == std::string::npos) return; if (FT->getParamType(1)->isIntegerTy()) ChangedType[1] = getSamplerType(F->getParent()); } SPIRVInstruction * LLVMToSPIRV::transBuiltinToInst(const std::string& DemangledName, const std::string &MangledName, CallInst* CI, SPIRVBasicBlock* BB) { SmallVector<std::string, 2> Dec; auto OC = getSPIRVFuncOC(DemangledName, &Dec); if (OC == OpNop) return nullptr; auto Inst = transBuiltinToInstWithoutDecoration(OC, CI, BB); addDecorations(Inst, Dec); return Inst; } bool LLVMToSPIRV::transExecutionMode() { if (auto NMD = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::ExecutionMode)) { while (!NMD.atEnd()) { unsigned EMode = ~0U; Function *F = nullptr; auto N = NMD.nextOp(); /* execution mode MDNode */ N.get(F).get(EMode); SPIRVFunction *BF = static_cast<SPIRVFunction *>(getTranslatedValue(F)); assert(BF && "Invalid kernel function"); if (!BF) return false; switch (EMode) { case spv::ExecutionModeContractionOff: case spv::ExecutionModeInitializer: case spv::ExecutionModeFinalizer: BF->addExecutionMode(new SPIRVExecutionMode(BF, static_cast<ExecutionMode>(EMode))); break; case spv::ExecutionModeLocalSize: case spv::ExecutionModeLocalSizeHint: { unsigned X, Y, Z; N.get(X).get(Y).get(Z); BF->addExecutionMode(new SPIRVExecutionMode(BF, static_cast<ExecutionMode>(EMode), X, Y, Z)); } break; case spv::ExecutionModeVecTypeHint: case spv::ExecutionModeSubgroupSize: case spv::ExecutionModeSubgroupsPerWorkgroup: { unsigned X; N.get(X); BF->addExecutionMode(new SPIRVExecutionMode(BF, static_cast<ExecutionMode>(EMode), X)); } break; default: llvm_unreachable("invalid execution mode"); } } } return true; } bool LLVMToSPIRV::transOCLKernelMetadata() { NamedMDNode *KernelMDs = M->getNamedMetadata(SPIR_MD_KERNELS); std::vector<std::string> argAccessQual; if (!KernelMDs) return true; for (unsigned I = 0, E = KernelMDs->getNumOperands(); I < E; ++I) { MDNode *KernelMD = KernelMDs->getOperand(I); if (KernelMD->getNumOperands() == 0) continue; Function *Kernel = mdconst::dyn_extract<Function>(KernelMD->getOperand(0)); SPIRVFunction *BF = static_cast<SPIRVFunction *>(getTranslatedValue(Kernel)); assert(BF && "Kernel function should be translated first"); assert(Kernel && oclIsKernel(Kernel) && "Invalid kernel calling convention or metadata"); for (unsigned MI = 1, ME = KernelMD->getNumOperands(); MI < ME; ++MI) { MDNode *MD = dyn_cast<MDNode>(KernelMD->getOperand(MI)); if (!MD) continue; MDString *NameMD = dyn_cast<MDString>(MD->getOperand(0)); if (!NameMD) continue; StringRef Name = NameMD->getString(); if (Name == SPIR_MD_KERNEL_ARG_TYPE_QUAL) { foreachKernelArgMD(MD, BF, [](const std::string &Str, SPIRVFunctionParameter *BA){ if (Str.find("volatile") != std::string::npos) BA->addDecorate(new SPIRVDecorate(DecorationVolatile, BA)); if (Str.find("restrict") != std::string::npos) BA->addDecorate(new SPIRVDecorate(DecorationFuncParamAttr, BA, FunctionParameterAttributeNoAlias)); if (Str.find("const") != std::string::npos) BA->addDecorate(new SPIRVDecorate(DecorationFuncParamAttr, BA, FunctionParameterAttributeNoWrite)); }); } else if (Name == SPIR_MD_KERNEL_ARG_NAME) { foreachKernelArgMD(MD, BF, [=](const std::string &Str, SPIRVFunctionParameter *BA){ BM->setName(BA, Str); }); } } } return true; } bool LLVMToSPIRV::transSourceLanguage() { auto Src = getSPIRVSource(M); SrcLang = std::get<0>(Src); SrcLangVer = std::get<1>(Src); BM->setSourceLanguage(static_cast<SourceLanguage>(SrcLang), SrcLangVer); return true; } bool LLVMToSPIRV::transExtension() { if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::Extension)) { while (!N.atEnd()) { std::string S; N.nextOp().get(S); assert(!S.empty() && "Invalid extension"); BM->getExtension().insert(S); } } if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::SourceExtension)) { while (!N.atEnd()) { std::string S; N.nextOp().get(S); assert(!S.empty() && "Invalid extension"); BM->getSourceExtension().insert(S); } } for (auto &I:map<SPIRVCapabilityKind>(rmap<OclExt::Kind>(BM->getExtension()))) BM->addCapability(I); return true; } void LLVMToSPIRV::dumpUsers(Value* V) { SPIRVDBG(dbgs() << "Users of " << *V << " :\n"); for (auto UI = V->user_begin(), UE = V->user_end(); UI != UE; ++UI) SPIRVDBG(dbgs() << " " << **UI << '\n'); } Op LLVMToSPIRV::transBoolOpCode(SPIRVValue* Opn, Op OC) { if (!Opn->getType()->isTypeVectorOrScalarBool()) return OC; IntBoolOpMap::find(OC, &OC); return OC; } SPIRVInstruction * LLVMToSPIRV::transBuiltinToInstWithoutDecoration(Op OC, CallInst* CI, SPIRVBasicBlock* BB) { if (isGroupOpCode(OC)) BM->addCapability(CapabilityGroups); switch (OC) { case OpControlBarrier: { auto BArgs = transValue(getArguments(CI), BB); return BM->addControlBarrierInst( BArgs[0], BArgs[1], BArgs[2], BB); } break; case OpGroupAsyncCopy: { auto BArgs = transValue(getArguments(CI), BB); return BM->addAsyncGroupCopy(BArgs[0], BArgs[1], BArgs[2], BArgs[3], BArgs[4], BArgs[5], BB); } break; default: { if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit) { return BM->addUnaryInst(OC, transType(CI->getType()), transValue(CI->getArgOperand(0), BB), BB); } else if (isCmpOpCode(OC)) { assert(CI && CI->getNumArgOperands() == 2 && "Invalid call inst"); auto ResultTy = CI->getType(); Type *BoolTy = IntegerType::getInt1Ty(M->getContext()); auto IsVector = ResultTy->isVectorTy(); if (IsVector) BoolTy = VectorType::get(BoolTy, ResultTy->getVectorNumElements()); auto BBT = transType(BoolTy); auto Cmp = BM->addCmpInst(OC, BBT, transValue(CI->getArgOperand(0), BB), transValue(CI->getArgOperand(1), BB), BB); auto Zero = transValue(Constant::getNullValue(ResultTy), BB); auto One = transValue( IsVector ? Constant::getAllOnesValue(ResultTy) : getInt32(M, 1), BB); return BM->addSelectInst(Cmp, One, Zero, BB); } else if (isBinaryOpCode(OC)) { assert(CI && CI->getNumArgOperands() == 2 && "Invalid call inst"); return BM->addBinaryInst(OC, transType(CI->getType()), transValue(CI->getArgOperand(0), BB), transValue(CI->getArgOperand(1), BB), BB); } else if (CI->getNumArgOperands() == 1 && !CI->getType()->isVoidTy() && !hasExecScope(OC) && !isAtomicOpCode(OC)) { return BM->addUnaryInst(OC, transType(CI->getType()), transValue(CI->getArgOperand(0), BB), BB); } else { auto Args = getArguments(CI); SPIRVType *SPRetTy = nullptr; Type *RetTy = CI->getType(); auto F = CI->getCalledFunction(); if (!RetTy->isVoidTy()) { SPRetTy = transType(RetTy); } else if (Args.size() > 0 && F->arg_begin()->hasStructRetAttr()) { SPRetTy = transType(F->arg_begin()->getType()->getPointerElementType()); Args.erase(Args.begin()); } auto SPI = BM->addInstTemplate(OC, BB, SPRetTy); std::vector<SPIRVWord> SPArgs; for (size_t I = 0, E = Args.size(); I != E; ++I) { assert((!isFunctionPointerType(Args[I]->getType()) || isa<Function>(Args[I])) && "Invalid function pointer argument"); SPArgs.push_back(SPI->isOperandLiteral(I) ? cast<ConstantInt>(Args[I])->getZExtValue() : transValue(Args[I], BB)->getId()); } SPI->setOpWordsAndValidate(SPArgs); if (!SPRetTy || !SPRetTy->isTypeStruct()) return SPI; std::vector<SPIRVWord> Mem; SPIRVDBG(spvdbgs() << *SPI << '\n'); return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), SPI, Mem, BB); } } } return nullptr; } SPIRVId LLVMToSPIRV::addInt32(int I) { return transValue(getInt32(M, I), nullptr, false)->getId(); } SPIRV::SPIRVLinkageTypeKind LLVMToSPIRV::transLinkageType(const GlobalValue* GV) { if(GV->isDeclarationForLinker()) return SPIRVLinkageTypeKind::LinkageTypeImport; if(GV->hasInternalLinkage() || GV->hasPrivateLinkage()) return SPIRVLinkageTypeKind::LinkageTypeInternal; return SPIRVLinkageTypeKind::LinkageTypeExport; } } // end of SPIRV namespace char LLVMToSPIRV::ID = 0; INITIALIZE_PASS_BEGIN(LLVMToSPIRV, "llvmtospv", "Translate LLVM to SPIR-V", false, false) INITIALIZE_PASS_DEPENDENCY(OCLTypeToSPIRV) INITIALIZE_PASS_END(LLVMToSPIRV, "llvmtospv", "Translate LLVM to SPIR-V", false, false) ModulePass *llvm::createLLVMToSPIRV(SPIRVModule *SMod) { return new LLVMToSPIRV(SMod); } void addPassesForSPIRV(legacy::PassManager &PassMgr) { if (SPIRVMemToReg) PassMgr.add(createPromoteMemoryToRegisterPass()); PassMgr.add(createTransOCLMD()); PassMgr.add(createOCL21ToSPIRV()); PassMgr.add(createSPIRVLowerOCLBlocks()); PassMgr.add(createOCLTypeToSPIRV()); PassMgr.add(createOCL20ToSPIRV()); PassMgr.add(createSPIRVRegularizeLLVM()); PassMgr.add(createSPIRVLowerConstExpr()); PassMgr.add(createSPIRVLowerBool()); } bool llvm::WriteSPIRV(Module *M, llvm::raw_ostream &OS, std::string &ErrMsg) { std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule()); legacy::PassManager PassMgr; addPassesForSPIRV(PassMgr); PassMgr.add(createLLVMToSPIRV(BM.get())); PassMgr.run(*M); if (BM->getError(ErrMsg) != SPIRVEC_Success) return false; OS << *BM; return true; } bool llvm::RegularizeLLVMForSPIRV(Module *M, std::string &ErrMsg) { std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule()); legacy::PassManager PassMgr; addPassesForSPIRV(PassMgr); PassMgr.run(*M); return true; }