//===- SPIRVModule.cpp - Class to represent SPIR-V module --------*- C++ -*-===// // // The LLVM/SPIRV Translator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // // Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal with the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimers in the documentation // and/or other materials provided with the distribution. // Neither the names of Advanced Micro Devices, Inc., nor the names of its // contributors may be used to endorse or promote products derived from this // Software without specific prior written permission. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // //===----------------------------------------------------------------------===// /// \file /// /// This file implements Module class for SPIR-V. /// //===----------------------------------------------------------------------===// #include "SPIRVModule.h" #include "SPIRVDebug.h" #include "SPIRVEntry.h" #include "SPIRVType.h" #include "SPIRVValue.h" #include "SPIRVExtInst.h" #include "SPIRVFunction.h" #include "SPIRVInstruction.h" #include "SPIRVStream.h" #include <set> #include <unordered_map> #include <unordered_set> namespace SPIRV{ SPIRVModule::SPIRVModule():AutoAddCapability(true), ValidateCapability(false) {} SPIRVModule::~SPIRVModule() {} class SPIRVModuleImpl : public SPIRVModule { public: SPIRVModuleImpl():SPIRVModule(), NextId(1), SPIRVVersion(SPIRV_1_0), GeneratorId(SPIRVGEN_KhronosLLVMSPIRVTranslator), GeneratorVer(0), InstSchema(SPIRVISCH_Default), SrcLang(SourceLanguageOpenCL_C), SrcLangVer(102000), MemoryModel(MemoryModelOpenCL){ AddrModel = sizeof(size_t) == 32 ? AddressingModelPhysical32 : AddressingModelPhysical64; }; virtual ~SPIRVModuleImpl(); // Object query functions bool exist(SPIRVId) const; bool exist(SPIRVId, SPIRVEntry **) const; SPIRVId getId(SPIRVId Id = SPIRVID_INVALID, unsigned Increment = 1); virtual SPIRVEntry *getEntry(SPIRVId Id) const; bool hasDebugInfo() const { return !LineVec.empty();} // Error handling functions SPIRVErrorLog &getErrorLog() { return ErrLog;} SPIRVErrorCode getError(std::string &ErrMsg) { return ErrLog.getError(ErrMsg);} // Module query functions SPIRVAddressingModelKind getAddressingModel() { return AddrModel;} SPIRVExtInstSetKind getBuiltinSet(SPIRVId SetId) const; const SPIRVCapMap &getCapability() const { return CapMap; } bool hasCapability(SPIRVCapabilityKind Cap) const { return CapMap.find(Cap) != CapMap.end(); } std::set<std::string> &getExtension() { return SPIRVExt;} SPIRVFunction *getFunction(unsigned I) const { return FuncVec[I];} SPIRVVariable *getVariable(unsigned I) const { return VariableVec[I];} virtual SPIRVValue *getValue(SPIRVId TheId) const; virtual std::vector<SPIRVValue *> getValues(const std::vector<SPIRVId>&)const; virtual std::vector<SPIRVId> getIds(const std::vector<SPIRVEntry *>&)const; virtual std::vector<SPIRVId> getIds(const std::vector<SPIRVValue *>&)const; virtual SPIRVType *getValueType(SPIRVId TheId)const; virtual std::vector<SPIRVType *> getValueTypes(const std::vector<SPIRVId>&) const; SPIRVMemoryModelKind getMemoryModel() const { return MemoryModel;} virtual SPIRVConstant* getLiteralAsConstant(unsigned Literal); unsigned getNumEntryPoints(SPIRVExecutionModelKind EM) const { auto Loc = EntryPointVec.find(EM); if (Loc == EntryPointVec.end()) return 0; return Loc->second.size(); } SPIRVFunction *getEntryPoint(SPIRVExecutionModelKind EM, unsigned I) const { auto Loc = EntryPointVec.find(EM); if (Loc == EntryPointVec.end()) return nullptr; assert(I < Loc->second.size()); return get<SPIRVFunction>(Loc->second[I]); } unsigned getNumFunctions() const { return FuncVec.size();} unsigned getNumVariables() const { return VariableVec.size();} SourceLanguage getSourceLanguage(SPIRVWord * Ver = nullptr) const { if (Ver) *Ver = SrcLangVer; return SrcLang; } std::set<std::string> &getSourceExtension() { return SrcExtension;} bool isEntryPoint(SPIRVExecutionModelKind, SPIRVId EP) const; unsigned short getGeneratorId() const { return GeneratorId; } unsigned short getGeneratorVer() const { return GeneratorVer; } SPIRVWord getSPIRVVersion() const { return SPIRVVersion; } // Module changing functions bool importBuiltinSet(const std::string &, SPIRVId *); bool importBuiltinSetWithId(const std::string &, SPIRVId); void optimizeDecorates(); void setAddressingModel(SPIRVAddressingModelKind AM) { AddrModel = AM;} void setAlignment(SPIRVValue *, SPIRVWord); void setMemoryModel(SPIRVMemoryModelKind MM) { MemoryModel = MM; if (MemoryModel == spv::MemoryModelOpenCL) addCapability(CapabilityKernel); } void setName(SPIRVEntry *E, const std::string &Name); void setSourceLanguage(SourceLanguage Lang, SPIRVWord Ver) { SrcLang = Lang; SrcLangVer = Ver; } void setGeneratorId(unsigned short Id) { GeneratorId = Id; } void setGeneratorVer(unsigned short Ver) { GeneratorVer = Ver; } void resolveUnknownStructFields(); void setSPIRVVersion(SPIRVWord Ver) override { SPIRVVersion = Ver; } // Object creation functions template<class T> void addTo(std::vector<T *> &V, SPIRVEntry *E); virtual SPIRVEntry *addEntry(SPIRVEntry *E); virtual SPIRVBasicBlock *addBasicBlock(SPIRVFunction *, SPIRVId); virtual SPIRVString *getString(const std::string &Str); virtual SPIRVMemberName *addMemberName(SPIRVTypeStruct *ST, SPIRVWord MemberNumber, const std::string &Name); virtual void addUnknownStructField(SPIRVTypeStruct *Struct, unsigned I, SPIRVId ID); virtual SPIRVLine *addLine(SPIRVEntry *E, SPIRVString *FileName, SPIRVWord Line, SPIRVWord Column); virtual void addCapability(SPIRVCapabilityKind); virtual void addCapabilityInternal(SPIRVCapabilityKind); virtual const SPIRVDecorateGeneric *addDecorate(const SPIRVDecorateGeneric *); virtual SPIRVDecorationGroup *addDecorationGroup(); virtual SPIRVDecorationGroup *addDecorationGroup(SPIRVDecorationGroup *Group); virtual SPIRVGroupDecorate *addGroupDecorate(SPIRVDecorationGroup *Group, const std::vector<SPIRVEntry *> &Targets); virtual SPIRVGroupDecorateGeneric *addGroupDecorateGeneric( SPIRVGroupDecorateGeneric *GDec); virtual SPIRVGroupMemberDecorate *addGroupMemberDecorate( SPIRVDecorationGroup *Group, const std::vector<SPIRVEntry *> &Targets); virtual void addEntryPoint(SPIRVExecutionModelKind ExecModel, SPIRVId EntryPoint); virtual SPIRVForward *addForward(SPIRVType *Ty); virtual SPIRVForward *addForward(SPIRVId, SPIRVType *Ty); virtual SPIRVFunction *addFunction(SPIRVFunction *); virtual SPIRVFunction *addFunction(SPIRVTypeFunction *, SPIRVId); virtual SPIRVEntry *replaceForward(SPIRVForward *, SPIRVEntry *); // Type creation functions template<class T> T * addType(T *Ty); virtual SPIRVTypeArray *addArrayType(SPIRVType *, SPIRVConstant *); virtual SPIRVTypeBool *addBoolType(); virtual SPIRVTypeFloat *addFloatType(unsigned BitWidth); virtual SPIRVTypeFunction *addFunctionType(SPIRVType *, const std::vector<SPIRVType *> &); virtual SPIRVTypeInt *addIntegerType(unsigned BitWidth); virtual SPIRVTypeOpaque *addOpaqueType(const std::string &); virtual SPIRVTypePointer *addPointerType(SPIRVStorageClassKind, SPIRVType *); virtual SPIRVTypeImage *addImageType(SPIRVType *, const SPIRVTypeImageDescriptor &); virtual SPIRVTypeImage *addImageType(SPIRVType *, const SPIRVTypeImageDescriptor &, SPIRVAccessQualifierKind); virtual SPIRVTypeSampler *addSamplerType(); virtual SPIRVTypePipeStorage *addPipeStorageType(); virtual SPIRVTypeSampledImage *addSampledImageType(SPIRVTypeImage *T); virtual SPIRVTypeStruct *openStructType(unsigned, const std::string &); virtual void closeStructType(SPIRVTypeStruct *T, bool); virtual SPIRVTypeVector *addVectorType(SPIRVType *, SPIRVWord); virtual SPIRVType *addOpaqueGenericType(Op); virtual SPIRVTypeDeviceEvent *addDeviceEventType(); virtual SPIRVTypeQueue *addQueueType(); virtual SPIRVTypePipe *addPipeType(); virtual SPIRVTypeVoid *addVoidType(); virtual void createForwardPointers(); // Constant creation functions virtual SPIRVInstruction *addBranchInst(SPIRVLabel *, SPIRVBasicBlock *); virtual SPIRVInstruction *addBranchConditionalInst(SPIRVValue *, SPIRVLabel *, SPIRVLabel *, SPIRVBasicBlock *); virtual SPIRVValue *addCompositeConstant(SPIRVType *, const std::vector<SPIRVValue*>&); virtual SPIRVValue *addConstant(SPIRVValue *); virtual SPIRVValue *addConstant(SPIRVType *, uint64_t); virtual SPIRVValue *addDoubleConstant(SPIRVTypeFloat *, double); virtual SPIRVValue *addFloatConstant(SPIRVTypeFloat *, float); virtual SPIRVValue *addIntegerConstant(SPIRVTypeInt *, uint64_t); virtual SPIRVValue *addNullConstant(SPIRVType *); virtual SPIRVValue *addUndef(SPIRVType *TheType); virtual SPIRVValue *addSamplerConstant(SPIRVType *TheType, SPIRVWord AddrMode, SPIRVWord ParametricMode, SPIRVWord FilterMode); virtual SPIRVValue* addPipeStorageConstant(SPIRVType* TheType, SPIRVWord PacketSize, SPIRVWord PacketAlign, SPIRVWord Capacity); // Instruction creation functions virtual SPIRVInstruction *addPtrAccessChainInst(SPIRVType *, SPIRVValue *, std::vector<SPIRVValue *>, SPIRVBasicBlock *, bool); virtual SPIRVInstruction *addAsyncGroupCopy(SPIRVValue *Scope, SPIRVValue *Dest, SPIRVValue *Src, SPIRVValue *NumElems, SPIRVValue *Stride, SPIRVValue *Event, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addExtInst(SPIRVType *, SPIRVWord, SPIRVWord, const std::vector<SPIRVWord> &, SPIRVBasicBlock *); virtual SPIRVInstruction *addExtInst(SPIRVType *, SPIRVWord, SPIRVWord, const std::vector<SPIRVValue *> &, SPIRVBasicBlock *); virtual SPIRVInstruction *addBinaryInst(Op, SPIRVType *, SPIRVValue *, SPIRVValue *, SPIRVBasicBlock *); virtual SPIRVInstruction *addCallInst(SPIRVFunction*, const std::vector<SPIRVWord> &, SPIRVBasicBlock *); virtual SPIRVInstruction *addCmpInst(Op, SPIRVType *, SPIRVValue *, SPIRVValue *, SPIRVBasicBlock *); virtual SPIRVInstruction *addLoadInst(SPIRVValue *, const std::vector<SPIRVWord>&, SPIRVBasicBlock *); virtual SPIRVInstruction *addPhiInst(SPIRVType *, std::vector<SPIRVValue *>, SPIRVBasicBlock *); virtual SPIRVInstruction *addCompositeExtractInst(SPIRVType *, SPIRVValue *, const std::vector<SPIRVWord>&, SPIRVBasicBlock *); virtual SPIRVInstruction *addCompositeInsertInst(SPIRVValue *Object, SPIRVValue *Composite, const std::vector<SPIRVWord>& Indices, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addCopyObjectInst(SPIRVType *TheType, SPIRVValue *Operand, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addCopyMemoryInst(SPIRVValue *, SPIRVValue *, const std::vector<SPIRVWord>&, SPIRVBasicBlock *); virtual SPIRVInstruction *addCopyMemorySizedInst(SPIRVValue *, SPIRVValue *, SPIRVValue *, const std::vector<SPIRVWord>&, SPIRVBasicBlock *); virtual SPIRVInstruction *addControlBarrierInst( SPIRVValue *ExecKind, SPIRVValue *MemKind, SPIRVValue *MemSema, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addGroupInst(Op OpCode, SPIRVType *Type, Scope Scope, const std::vector<SPIRVValue *> &Ops, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addInstruction(SPIRVInstruction *Inst, SPIRVBasicBlock *BB); virtual SPIRVInstTemplateBase *addInstTemplate(Op OC, SPIRVBasicBlock* BB, SPIRVType *Ty); virtual SPIRVInstTemplateBase *addInstTemplate(Op OC, const std::vector<SPIRVWord>& Ops, SPIRVBasicBlock* BB, SPIRVType *Ty); virtual SPIRVInstruction *addMemoryBarrierInst( Scope ScopeKind, SPIRVWord MemFlag, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addReturnInst(SPIRVBasicBlock *); virtual SPIRVInstruction *addReturnValueInst(SPIRVValue *, SPIRVBasicBlock *); virtual SPIRVInstruction *addSelectInst(SPIRVValue *, SPIRVValue *, SPIRVValue *, SPIRVBasicBlock *); virtual SPIRVInstruction *addStoreInst(SPIRVValue *, SPIRVValue *, const std::vector<SPIRVWord>&, SPIRVBasicBlock *); virtual SPIRVInstruction *addSwitchInst(SPIRVValue *, SPIRVBasicBlock *, const std::vector<std::pair<SPIRVWord, SPIRVBasicBlock *>>&, SPIRVBasicBlock *); virtual SPIRVInstruction *addUnaryInst(Op, SPIRVType *, SPIRVValue *, SPIRVBasicBlock *); virtual SPIRVInstruction *addVariable(SPIRVType *, bool, SPIRVLinkageTypeKind, SPIRVValue *, const std::string &, SPIRVStorageClassKind, SPIRVBasicBlock *); virtual SPIRVValue *addVectorShuffleInst(SPIRVType *Type, SPIRVValue *Vec1, SPIRVValue *Vec2, const std::vector<SPIRVWord> &Components, SPIRVBasicBlock *BB); virtual SPIRVInstruction *addVectorExtractDynamicInst(SPIRVValue *, SPIRVValue *, SPIRVBasicBlock *); virtual SPIRVInstruction *addVectorInsertDynamicInst(SPIRVValue *, SPIRVValue *, SPIRVValue*, SPIRVBasicBlock *); // I/O functions friend spv_ostream & operator<<(spv_ostream &O, SPIRVModule& M); friend std::istream & operator>>(std::istream &I, SPIRVModule& M); private: SPIRVErrorLog ErrLog; SPIRVId NextId; SPIRVWord SPIRVVersion; unsigned short GeneratorId; unsigned short GeneratorVer; SPIRVInstructionSchemaKind InstSchema; SourceLanguage SrcLang; SPIRVWord SrcLangVer; std::set<std::string> SrcExtension; std::set<std::string> SPIRVExt; SPIRVAddressingModelKind AddrModel; SPIRVMemoryModelKind MemoryModel; typedef std::map<SPIRVId, SPIRVEntry *> SPIRVIdToEntryMap; typedef std::vector<SPIRVEntry *> SPIRVEntryVector; typedef std::set<SPIRVId> SPIRVIdSet; typedef std::vector<SPIRVId> SPIRVIdVec; typedef std::vector<SPIRVFunction *> SPIRVFunctionVector; typedef std::vector<SPIRVTypeForwardPointer *> SPIRVForwardPointerVec; typedef std::vector<SPIRVType *> SPIRVTypeVec; typedef std::vector<SPIRVValue *> SPIRVConstantVector; typedef std::vector<SPIRVVariable *> SPIRVVariableVec; typedef std::vector<SPIRVString *> SPIRVStringVec; typedef std::vector<SPIRVMemberName *> SPIRVMemberNameVec; typedef std::vector<SPIRVLine *> SPIRVLineVec; typedef std::vector<SPIRVDecorationGroup *> SPIRVDecGroupVec; typedef std::vector<SPIRVGroupDecorateGeneric *> SPIRVGroupDecVec; typedef std::map<SPIRVId, SPIRVExtInstSetKind> SPIRVIdToBuiltinSetMap; typedef std::map<SPIRVExecutionModelKind, SPIRVIdSet> SPIRVExecModelIdSetMap; typedef std::map<SPIRVExecutionModelKind, SPIRVIdVec> SPIRVExecModelIdVecMap; typedef std::unordered_map<std::string, SPIRVString*> SPIRVStringMap; typedef std::map<SPIRVTypeStruct *, std::vector<std::pair<unsigned, SPIRVId>>> SPIRVUnknownStructFieldMap; SPIRVForwardPointerVec ForwardPointerVec; SPIRVTypeVec TypeVec; SPIRVIdToEntryMap IdEntryMap; SPIRVFunctionVector FuncVec; SPIRVConstantVector ConstVec; SPIRVVariableVec VariableVec; SPIRVEntryVector EntryNoId; // Entries without id SPIRVIdToBuiltinSetMap IdBuiltinMap; SPIRVIdSet NamedId; SPIRVStringVec StringVec; SPIRVMemberNameVec MemberNameVec; SPIRVLineVec LineVec; SPIRVDecorateSet DecorateSet; SPIRVDecGroupVec DecGroupVec; SPIRVGroupDecVec GroupDecVec; SPIRVExecModelIdSetMap EntryPointSet; SPIRVExecModelIdVecMap EntryPointVec; SPIRVStringMap StrMap; SPIRVCapMap CapMap; SPIRVUnknownStructFieldMap UnknownStructFieldMap; std::map<unsigned, SPIRVTypeInt*> IntTypeMap; std::map<unsigned, SPIRVConstant*> LiteralMap; void layoutEntry(SPIRVEntry* Entry); }; SPIRVModuleImpl::~SPIRVModuleImpl() { //ToDo: Fix bug causing crash //for (auto I:IdEntryMap) // delete I.second; // ToDo: Fix bug causing crash //for (auto I:EntryNoId) { // bildbgs() << "[delete] " << *I; // delete I; //} for (auto C : CapMap) delete C.second; } SPIRVLine* SPIRVModuleImpl::addLine(SPIRVEntry* E, SPIRVString* FileName, SPIRVWord Line, SPIRVWord Column) { auto L = add(new SPIRVLine(E, FileName->getId(), Line, Column)); E->setLine(L); return L; } // Creates decoration group and group decorates from decorates shared by // multiple targets. void SPIRVModuleImpl::optimizeDecorates() { SPIRVDBG(spvdbgs() << "[optimizeDecorates] begin\n"); for (auto I = DecorateSet.begin(), E = DecorateSet.end(); I != E;) { auto D = *I; SPIRVDBG(spvdbgs() << " check " << *D << '\n'); if (D->getOpCode() == OpMemberDecorate) { ++I; continue; } auto ER = DecorateSet.equal_range(D); SPIRVDBG(spvdbgs() << " equal range " << **ER.first << " to "; if (ER.second != DecorateSet.end()) spvdbgs() << **ER.second; else spvdbgs() << "end"; spvdbgs() << '\n'); if (std::distance(ER.first, ER.second) < 2) { I = ER.second; SPIRVDBG(spvdbgs() << " skip equal range \n"); continue; } SPIRVDBG(spvdbgs() << " add deco group. erase equal range\n"); auto G = new SPIRVDecorationGroup(this, getId()); std::vector<SPIRVId> Targets; Targets.push_back(D->getTargetId()); const_cast<SPIRVDecorateGeneric*>(D)->setTargetId(G->getId()); G->getDecorations().insert(D); for (I = ER.first; I != ER.second; ++I) { auto E = *I; if (*E == *D) continue; Targets.push_back(E->getTargetId()); } // WordCount is only 16 bits. We can only have 65535 - FixedWC targtets per // group. // For now, just skip using a group if the number of targets to too big if (Targets.size() < 65530) { DecorateSet.erase(ER.first, ER.second); auto GD = new SPIRVGroupDecorate(G, Targets); DecGroupVec.push_back(G); GroupDecVec.push_back(GD); } } } SPIRVValue* SPIRVModuleImpl::addSamplerConstant(SPIRVType* TheType, SPIRVWord AddrMode, SPIRVWord ParametricMode, SPIRVWord FilterMode) { return addConstant(new SPIRVConstantSampler(this, TheType, getId(), AddrMode, ParametricMode, FilterMode)); } SPIRVValue* SPIRVModuleImpl::addPipeStorageConstant(SPIRVType* TheType, SPIRVWord PacketSize, SPIRVWord PacketAlign, SPIRVWord Capacity) { return addConstant(new SPIRVConstantPipeStorage(this, TheType, getId(), PacketSize, PacketAlign, Capacity)); } void SPIRVModuleImpl::addCapability(SPIRVCapabilityKind Cap) { addCapabilities(SPIRV::getCapability(Cap)); SPIRVDBG(spvdbgs() << "addCapability: " << Cap << '\n'); if (hasCapability(Cap)) return; CapMap.insert(std::make_pair(Cap, new SPIRVCapability(this, Cap))); } void SPIRVModuleImpl::addCapabilityInternal(SPIRVCapabilityKind Cap) { if (AutoAddCapability) { if (hasCapability(Cap)) return; CapMap.insert(std::make_pair(Cap, new SPIRVCapability(this, Cap))); } } SPIRVConstant* SPIRVModuleImpl::getLiteralAsConstant(unsigned Literal) { auto Loc = LiteralMap.find(Literal); if (Loc != LiteralMap.end()) return Loc->second; auto Ty = addIntegerType(32); auto V = new SPIRVConstant(this, Ty, getId(), static_cast<uint64_t>(Literal)); LiteralMap[Literal] = V; addConstant(V); return V; } void SPIRVModuleImpl::layoutEntry(SPIRVEntry* E) { auto OC = E->getOpCode(); switch (OC) { case OpString: addTo(StringVec, E); break; case OpMemberName: addTo(MemberNameVec, E); break; case OpLine: addTo(LineVec, E); break; case OpVariable: { auto BV = static_cast<SPIRVVariable*>(E); if (!BV->getParent()) addTo(VariableVec, E); } break; default: if (isTypeOpCode(OC)) TypeVec.push_back(static_cast<SPIRVType*>(E)); else if (isConstantOpCode(OC)) ConstVec.push_back(static_cast<SPIRVConstant*>(E)); break; } } // Add an entry to the id to entry map. // Assert if the id is mapped to a different entry. // Certain entries need to be add to specific collectors to maintain // logic layout of SPIRV. SPIRVEntry * SPIRVModuleImpl::addEntry(SPIRVEntry *Entry) { assert(Entry && "Invalid entry"); if (Entry->hasId()) { SPIRVId Id = Entry->getId(); assert(Entry->getId() != SPIRVID_INVALID && "Invalid id"); SPIRVEntry *Mapped = nullptr; if (exist(Id, &Mapped)) { if (Mapped->getOpCode() == OpForward) { replaceForward(static_cast<SPIRVForward *>(Mapped), Entry); } else { assert(Mapped == Entry && "Id used twice"); } } else IdEntryMap[Id] = Entry; } else { EntryNoId.push_back(Entry); } Entry->setModule(this); layoutEntry(Entry); if (AutoAddCapability) { for (auto &I:Entry->getRequiredCapability()) { addCapability(I); } } if (ValidateCapability) { for (auto &I:Entry->getRequiredCapability()) { (void) I; assert(CapMap.count(I)); } } return Entry; } bool SPIRVModuleImpl::exist(SPIRVId Id) const { return exist(Id, nullptr); } bool SPIRVModuleImpl::exist(SPIRVId Id, SPIRVEntry **Entry) const { assert (Id != SPIRVID_INVALID && "Invalid Id"); SPIRVIdToEntryMap::const_iterator Loc = IdEntryMap.find(Id); if (Loc == IdEntryMap.end()) return false; if (Entry) *Entry = Loc->second; return true; } // If Id is invalid, returns the next available id. // Otherwise returns the given id and adjust the next available id by increment. SPIRVId SPIRVModuleImpl::getId(SPIRVId Id, unsigned increment) { if (!isValidId(Id)) Id = NextId; else NextId = std::max(Id, NextId); NextId += increment; return Id; } SPIRVEntry * SPIRVModuleImpl::getEntry(SPIRVId Id) const { assert (Id != SPIRVID_INVALID && "Invalid Id"); SPIRVIdToEntryMap::const_iterator Loc = IdEntryMap.find(Id); assert (Loc != IdEntryMap.end() && "Id is not in map"); return Loc->second; } SPIRVExtInstSetKind SPIRVModuleImpl::getBuiltinSet(SPIRVId SetId) const { auto Loc = IdBuiltinMap.find(SetId); assert(Loc != IdBuiltinMap.end() && "Invalid builtin set id"); return Loc->second; } bool SPIRVModuleImpl::isEntryPoint(SPIRVExecutionModelKind ExecModel, SPIRVId EP) const { assert(isValid(ExecModel) && "Invalid execution model"); assert(EP != SPIRVID_INVALID && "Invalid function id"); auto Loc = EntryPointSet.find(ExecModel); if (Loc == EntryPointSet.end()) return false; return Loc->second.count(EP); } // Module change functions bool SPIRVModuleImpl::importBuiltinSet(const std::string& BuiltinSetName, SPIRVId *BuiltinSetId) { SPIRVId TmpBuiltinSetId = getId(); if (!importBuiltinSetWithId(BuiltinSetName, TmpBuiltinSetId)) return false; if (BuiltinSetId) *BuiltinSetId = TmpBuiltinSetId; return true; } bool SPIRVModuleImpl::importBuiltinSetWithId(const std::string& BuiltinSetName, SPIRVId BuiltinSetId) { SPIRVExtInstSetKind BuiltinSet = SPIRVEIS_Count; SPIRVCKRT(SPIRVBuiltinSetNameMap::rfind(BuiltinSetName, &BuiltinSet), InvalidBuiltinSetName, "Actual is " + BuiltinSetName); IdBuiltinMap[BuiltinSetId] = BuiltinSet; return true; } void SPIRVModuleImpl::setAlignment(SPIRVValue *V, SPIRVWord A) { V->setAlignment(A); } void SPIRVModuleImpl::setName(SPIRVEntry *E, const std::string &Name) { E->setName(Name); if (!E->hasId()) return; if (!Name.empty()) NamedId.insert(E->getId()); else NamedId.erase(E->getId()); } void SPIRVModuleImpl::resolveUnknownStructFields() { for (auto &KV : UnknownStructFieldMap) { auto *Struct = KV.first; for (auto &Indices : KV.second) { unsigned I = Indices.first; SPIRVId ID = Indices.second; auto Ty = static_cast<SPIRVType *>(getEntry(ID)); Struct->setMemberType(I, Ty); } } } // Type creation functions template<class T> T * SPIRVModuleImpl::addType(T *Ty) { add(Ty); if (!Ty->getName().empty()) setName(Ty, Ty->getName()); return Ty; } SPIRVTypeVoid * SPIRVModuleImpl::addVoidType() { return addType(new SPIRVTypeVoid(this, getId())); } SPIRVTypeArray * SPIRVModuleImpl::addArrayType(SPIRVType *ElementType, SPIRVConstant *Length) { return addType(new SPIRVTypeArray(this, getId(), ElementType, Length)); } SPIRVTypeBool * SPIRVModuleImpl::addBoolType() { return addType(new SPIRVTypeBool(this, getId())); } SPIRVTypeInt * SPIRVModuleImpl::addIntegerType(unsigned BitWidth) { auto Loc = IntTypeMap.find(BitWidth); if (Loc != IntTypeMap.end()) return Loc->second; auto Ty = new SPIRVTypeInt(this, getId(), BitWidth, false); IntTypeMap[BitWidth] = Ty; return addType(Ty); } SPIRVTypeFloat * SPIRVModuleImpl::addFloatType(unsigned BitWidth) { SPIRVTypeFloat *T = addType(new SPIRVTypeFloat(this, getId(), BitWidth)); return T; } SPIRVTypePointer * SPIRVModuleImpl::addPointerType(SPIRVStorageClassKind StorageClass, SPIRVType *ElementType) { return addType(new SPIRVTypePointer(this, getId(), StorageClass, ElementType)); } SPIRVTypeFunction * SPIRVModuleImpl::addFunctionType(SPIRVType *ReturnType, const std::vector<SPIRVType *>& ParameterTypes) { return addType(new SPIRVTypeFunction(this, getId(), ReturnType, ParameterTypes)); } SPIRVTypeOpaque* SPIRVModuleImpl::addOpaqueType(const std::string& Name) { return addType(new SPIRVTypeOpaque(this, getId(), Name)); } SPIRVTypeStruct *SPIRVModuleImpl::openStructType(unsigned NumMembers, const std::string &Name) { auto T = new SPIRVTypeStruct(this, getId(), NumMembers, Name); return T; } void SPIRVModuleImpl::closeStructType(SPIRVTypeStruct *T, bool Packed) { addType(T); T->setPacked(Packed); } SPIRVTypeVector* SPIRVModuleImpl::addVectorType(SPIRVType* CompType, SPIRVWord CompCount) { return addType(new SPIRVTypeVector(this, getId(), CompType, CompCount)); } SPIRVType * SPIRVModuleImpl::addOpaqueGenericType(Op TheOpCode) { return addType(new SPIRVTypeOpaqueGeneric(TheOpCode, this, getId())); } SPIRVTypeDeviceEvent * SPIRVModuleImpl::addDeviceEventType() { return addType(new SPIRVTypeDeviceEvent(this, getId())); } SPIRVTypeQueue * SPIRVModuleImpl::addQueueType() { return addType(new SPIRVTypeQueue(this, getId())); } SPIRVTypePipe* SPIRVModuleImpl::addPipeType() { return addType(new SPIRVTypePipe(this, getId())); } SPIRVTypeImage * SPIRVModuleImpl::addImageType(SPIRVType *SampledType, const SPIRVTypeImageDescriptor &Desc) { return addType(new SPIRVTypeImage(this, getId(), SampledType ? SampledType->getId() : 0, Desc)); } SPIRVTypeImage * SPIRVModuleImpl::addImageType(SPIRVType *SampledType, const SPIRVTypeImageDescriptor &Desc, SPIRVAccessQualifierKind Acc) { return addType(new SPIRVTypeImage(this, getId(), SampledType ? SampledType->getId() : 0, Desc, Acc)); } SPIRVTypeSampler * SPIRVModuleImpl::addSamplerType() { return addType(new SPIRVTypeSampler(this, getId())); } SPIRVTypePipeStorage* SPIRVModuleImpl::addPipeStorageType() { return addType(new SPIRVTypePipeStorage(this, getId())); } SPIRVTypeSampledImage * SPIRVModuleImpl::addSampledImageType(SPIRVTypeImage *T) { return addType(new SPIRVTypeSampledImage(this, getId(), T)); } void SPIRVModuleImpl::createForwardPointers() { std::unordered_set<SPIRVId> Seen; for (auto *T : TypeVec) { if (T->hasId()) Seen.insert(T->getId()); if (!T->isTypeStruct()) continue; auto ST = static_cast<SPIRVTypeStruct *>(T); for (unsigned i = 0; i < ST->getStructMemberCount(); ++i) { auto MemberTy = ST->getStructMemberType(i); if (!MemberTy->isTypePointer()) continue; auto Ptr = static_cast<SPIRVTypePointer *>(MemberTy); if (Seen.find(Ptr->getId()) == Seen.end()) { ForwardPointerVec.push_back(new SPIRVTypeForwardPointer( this, Ptr, Ptr->getPointerStorageClass())); } } } } SPIRVFunction * SPIRVModuleImpl::addFunction(SPIRVFunction *Func) { FuncVec.push_back(add(Func)); return Func; } SPIRVFunction * SPIRVModuleImpl::addFunction(SPIRVTypeFunction *FuncType, SPIRVId Id) { return addFunction(new SPIRVFunction(this, FuncType, getId(Id, FuncType->getNumParameters() + 1))); } SPIRVBasicBlock * SPIRVModuleImpl::addBasicBlock(SPIRVFunction *Func, SPIRVId Id) { return Func->addBasicBlock(new SPIRVBasicBlock(getId(Id), Func)); } const SPIRVDecorateGeneric * SPIRVModuleImpl::addDecorate(const SPIRVDecorateGeneric *Dec) { SPIRVId Id = Dec->getTargetId(); SPIRVEntry *Target = nullptr; bool Found = exist(Id, &Target); (void) Found; assert (Found && "Decorate target does not exist"); if (!Dec->getOwner()) DecorateSet.insert(Dec); addCapabilities(Dec->getRequiredCapability()); return Dec; } void SPIRVModuleImpl::addEntryPoint(SPIRVExecutionModelKind ExecModel, SPIRVId EntryPoint){ assert(isValid(ExecModel) && "Invalid execution model"); assert(EntryPoint != SPIRVID_INVALID && "Invalid entry point"); EntryPointSet[ExecModel].insert(EntryPoint); EntryPointVec[ExecModel].push_back(EntryPoint); addCapabilities(SPIRV::getCapability(ExecModel)); } SPIRVForward * SPIRVModuleImpl::addForward(SPIRVType *Ty) { return add(new SPIRVForward(this, Ty, getId())); } SPIRVForward * SPIRVModuleImpl::addForward(SPIRVId Id, SPIRVType *Ty) { return add(new SPIRVForward(this, Ty, Id)); } SPIRVEntry * SPIRVModuleImpl::replaceForward(SPIRVForward *Forward, SPIRVEntry *Entry) { SPIRVId Id = Entry->getId(); SPIRVId ForwardId = Forward->getId(); if (ForwardId == Id) IdEntryMap[Id] = Entry; else { auto Loc = IdEntryMap.find(Id); assert(Loc != IdEntryMap.end()); IdEntryMap.erase(Loc); Entry->setId(ForwardId); IdEntryMap[ForwardId] = Entry; } // Annotations include name, decorations, execution modes Entry->takeAnnotations(Forward); delete Forward; return Entry; } SPIRVValue * SPIRVModuleImpl::addConstant(SPIRVValue *C) { return add(C); } SPIRVValue * SPIRVModuleImpl::addConstant(SPIRVType *Ty, uint64_t V) { if (Ty->isTypeBool()) { if (V) return addConstant(new SPIRVConstantTrue(this, Ty, getId())); else return addConstant(new SPIRVConstantFalse(this, Ty, getId())); } if (Ty->isTypeInt()) return addIntegerConstant(static_cast<SPIRVTypeInt*>(Ty), V); return addConstant(new SPIRVConstant(this, Ty, getId(), V)); } SPIRVValue * SPIRVModuleImpl::addIntegerConstant(SPIRVTypeInt *Ty, uint64_t V) { if (Ty->getBitWidth() == 32) { unsigned I32 = V; assert(I32 == V && "Integer value truncated"); return getLiteralAsConstant(I32); } return addConstant(new SPIRVConstant(this, Ty, getId(), V)); } SPIRVValue * SPIRVModuleImpl::addFloatConstant(SPIRVTypeFloat *Ty, float V) { return addConstant(new SPIRVConstant(this, Ty, getId(), V)); } SPIRVValue * SPIRVModuleImpl::addDoubleConstant(SPIRVTypeFloat *Ty, double V) { return addConstant(new SPIRVConstant(this, Ty, getId(), V)); } SPIRVValue * SPIRVModuleImpl::addNullConstant(SPIRVType *Ty) { return addConstant(new SPIRVConstantNull(this, Ty, getId())); } SPIRVValue * SPIRVModuleImpl::addCompositeConstant(SPIRVType *Ty, const std::vector<SPIRVValue*>& Elements) { return addConstant(new SPIRVConstantComposite(this, Ty, getId(), Elements)); } SPIRVValue * SPIRVModuleImpl::addUndef(SPIRVType *TheType) { return addConstant(new SPIRVUndef(this, TheType, getId())); } // Instruction creation functions SPIRVInstruction * SPIRVModuleImpl::addStoreInst(SPIRVValue *Target, SPIRVValue *Source, const std::vector<SPIRVWord> &TheMemoryAccess, SPIRVBasicBlock *BB) { return BB->addInstruction(new SPIRVStore(Target->getId(), Source->getId(), TheMemoryAccess, BB)); } SPIRVInstruction * SPIRVModuleImpl::addSwitchInst(SPIRVValue *Select, SPIRVBasicBlock *Default, const std::vector<std::pair<SPIRVWord, SPIRVBasicBlock *>>& Pairs, SPIRVBasicBlock *BB) { return BB->addInstruction(new SPIRVSwitch(Select, Default, Pairs, BB)); } SPIRVInstruction * SPIRVModuleImpl::addGroupInst(Op OpCode, SPIRVType *Type, Scope Scope, const std::vector<SPIRVValue *> &Ops, SPIRVBasicBlock *BB) { assert(!Type || !Type->isTypeVoid()); auto WordOps = getIds(Ops); WordOps.insert(WordOps.begin(), Scope); return addInstTemplate(OpCode, WordOps, BB, Type); } SPIRVInstruction * SPIRVModuleImpl::addInstruction(SPIRVInstruction *Inst, SPIRVBasicBlock *BB) { if (BB) return BB->addInstruction(Inst); if (Inst->getOpCode() != OpSpecConstantOp) Inst = createSpecConstantOpInst(Inst); return static_cast<SPIRVInstruction *>(addConstant(Inst)); } SPIRVInstruction * SPIRVModuleImpl::addLoadInst(SPIRVValue *Source, const std::vector<SPIRVWord> &TheMemoryAccess, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVLoad(getId(), Source->getId(), TheMemoryAccess, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addPhiInst(SPIRVType *Type, std::vector<SPIRVValue *> IncomingPairs, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVPhi(Type, getId(), IncomingPairs, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addExtInst(SPIRVType *TheType, SPIRVWord BuiltinSet, SPIRVWord EntryPoint, const std::vector<SPIRVWord> &Args, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVExtInst(TheType, getId(), BuiltinSet, EntryPoint, Args, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addExtInst(SPIRVType *TheType, SPIRVWord BuiltinSet, SPIRVWord EntryPoint, const std::vector<SPIRVValue *> &Args, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVExtInst(TheType, getId(), BuiltinSet, EntryPoint, Args, BB), BB); } SPIRVInstruction* SPIRVModuleImpl::addCallInst(SPIRVFunction* TheFunction, const std::vector<SPIRVWord> &TheArguments, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVFunctionCall(getId(), TheFunction, TheArguments, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addBinaryInst(Op TheOpCode, SPIRVType *Type, SPIRVValue *Op1, SPIRVValue *Op2, SPIRVBasicBlock *BB){ return addInstruction(SPIRVInstTemplateBase::create(TheOpCode, Type, getId(), getVec(Op1->getId(), Op2->getId()), BB, this), BB); } SPIRVInstruction * SPIRVModuleImpl::addReturnInst(SPIRVBasicBlock *BB) { return addInstruction(new SPIRVReturn(BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addReturnValueInst(SPIRVValue *ReturnValue, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVReturnValue(ReturnValue, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addUnaryInst(Op TheOpCode, SPIRVType *TheType, SPIRVValue *Op, SPIRVBasicBlock *BB) { return addInstruction(SPIRVInstTemplateBase::create(TheOpCode, TheType, getId(), getVec(Op->getId()), BB, this), BB); } SPIRVInstruction * SPIRVModuleImpl::addVectorExtractDynamicInst(SPIRVValue *TheVector, SPIRVValue *Index, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVVectorExtractDynamic(getId(), TheVector, Index, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addVectorInsertDynamicInst(SPIRVValue *TheVector, SPIRVValue *TheComponent, SPIRVValue*Index, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVVectorInsertDynamic(getId(), TheVector, TheComponent, Index, BB), BB); } SPIRVValue * SPIRVModuleImpl::addVectorShuffleInst(SPIRVType * Type, SPIRVValue *Vec1, SPIRVValue *Vec2, const std::vector<SPIRVWord> &Components, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVVectorShuffle(getId(), Type, Vec1, Vec2, Components, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addBranchInst(SPIRVLabel *TargetLabel, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVBranch(TargetLabel, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addBranchConditionalInst(SPIRVValue *Condition, SPIRVLabel *TrueLabel, SPIRVLabel *FalseLabel, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVBranchConditional(Condition, TrueLabel, FalseLabel, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addCmpInst(Op TheOpCode, SPIRVType *TheType, SPIRVValue *Op1, SPIRVValue *Op2, SPIRVBasicBlock *BB) { return addInstruction(SPIRVInstTemplateBase::create(TheOpCode, TheType, getId(), getVec(Op1->getId(), Op2->getId()), BB, this), BB); } SPIRVInstruction * SPIRVModuleImpl::addControlBarrierInst(SPIRVValue *ExecKind, SPIRVValue *MemKind, SPIRVValue *MemSema, SPIRVBasicBlock *BB) { return addInstruction( new SPIRVControlBarrier(ExecKind, MemKind, MemSema, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addMemoryBarrierInst(Scope ScopeKind, SPIRVWord MemFlag, SPIRVBasicBlock *BB) { return addInstruction(SPIRVInstTemplateBase::create(OpMemoryBarrier, nullptr, SPIRVID_INVALID, getVec(static_cast<SPIRVWord>(ScopeKind), MemFlag), BB, this), BB); } SPIRVInstruction * SPIRVModuleImpl::addSelectInst(SPIRVValue *Condition, SPIRVValue *Op1, SPIRVValue *Op2, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVSelect(getId(), Condition->getId(), Op1->getId(), Op2->getId(), BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addPtrAccessChainInst(SPIRVType *Type, SPIRVValue *Base, std::vector<SPIRVValue *> Indices, SPIRVBasicBlock *BB, bool IsInBounds){ return addInstruction(SPIRVInstTemplateBase::create( IsInBounds?OpInBoundsPtrAccessChain:OpPtrAccessChain, Type, getId(), getVec(Base->getId(), Base->getIds(Indices)), BB, this), BB); } SPIRVInstruction * SPIRVModuleImpl::addAsyncGroupCopy(SPIRVValue *Scope, SPIRVValue *Dest, SPIRVValue *Src, SPIRVValue *NumElems, SPIRVValue *Stride, SPIRVValue *Event, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVGroupAsyncCopy(Scope, getId(), Dest, Src, NumElems, Stride, Event, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addCompositeExtractInst(SPIRVType *Type, SPIRVValue *TheVector, const std::vector<SPIRVWord>& Indices, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVCompositeExtract(Type, getId(), TheVector, Indices, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addCompositeInsertInst(SPIRVValue *Object, SPIRVValue *Composite, const std::vector<SPIRVWord>& Indices, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVCompositeInsert(getId(), Object, Composite, Indices, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addCopyObjectInst(SPIRVType *TheType, SPIRVValue *Operand, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVCopyObject(TheType, getId(), Operand, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addCopyMemoryInst(SPIRVValue *TheTarget, SPIRVValue *TheSource, const std::vector<SPIRVWord> &TheMemoryAccess, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVCopyMemory(TheTarget, TheSource, TheMemoryAccess, BB), BB); } SPIRVInstruction * SPIRVModuleImpl::addCopyMemorySizedInst(SPIRVValue *TheTarget, SPIRVValue *TheSource, SPIRVValue *TheSize, const std::vector<SPIRVWord> &TheMemoryAccess, SPIRVBasicBlock *BB) { return addInstruction(new SPIRVCopyMemorySized(TheTarget, TheSource, TheSize, TheMemoryAccess, BB), BB); } SPIRVInstruction* SPIRVModuleImpl::addVariable(SPIRVType *Type, bool IsConstant, SPIRVLinkageTypeKind LinkageType, SPIRVValue *Initializer, const std::string &Name, SPIRVStorageClassKind StorageClass, SPIRVBasicBlock *BB) { SPIRVVariable *Variable = new SPIRVVariable(Type, getId(), Initializer, Name, StorageClass, BB, this); if (BB) return addInstruction(Variable, BB); add(Variable); if (LinkageType != LinkageTypeInternal) Variable->setLinkageType(LinkageType); Variable->setIsConstant(IsConstant); return Variable; } template<class T> spv_ostream & operator<< (spv_ostream &O, const std::vector<T *>& V) { for (auto &I: V) O << *I; return O; } template<class T, class B> spv_ostream & operator<< (spv_ostream &O, const std::multiset<T *, B>& V) { for (auto &I: V) O << *I; return O; } // To satisfy SPIR-V spec requirement: // "All operands must be declared before being used", // we do DFS based topological sort // https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search class TopologicalSort { enum DFSState : char { Unvisited, Discovered, Visited }; typedef std::vector<SPIRVType *> SPIRVTypeVec; typedef std::vector<SPIRVValue *> SPIRVConstantVector; typedef std::vector<SPIRVVariable *> SPIRVVariableVec; typedef std::vector<SPIRVTypeForwardPointer *> SPIRVForwardPointerVec; typedef std::function<bool(SPIRVEntry*, SPIRVEntry*)> IdComp; typedef std::map<SPIRVEntry*, DFSState, IdComp> EntryStateMapTy; SPIRVTypeVec TypeIntVec; SPIRVConstantVector ConstIntVec; SPIRVTypeVec TypeVec; SPIRVConstantVector ConstVec; SPIRVVariableVec VariableVec; const SPIRVForwardPointerVec& ForwardPointerVec; EntryStateMapTy EntryStateMap; friend spv_ostream & operator<<(spv_ostream &O, const TopologicalSort &S); // This method implements recursive depth-first search among all Entries in // EntryStateMap. Traversing entries and adding them to corresponding container // after visiting all dependent entries(post-order traversal) guarantees that // the entry's operands will appear in the container before the entry itslef. void visit(SPIRVEntry* E) { DFSState& State = EntryStateMap[E]; assert(State != Discovered && "Cyclic dependency detected"); if (State == Visited) return; State = Discovered; for (SPIRVEntry *Op : E->getNonLiteralOperands()) { auto Comp = [&Op](SPIRVTypeForwardPointer *FwdPtr) { return FwdPtr->getPointer() == Op; }; // Skip forward referenced pointers if (Op->getOpCode() == OpTypePointer && find_if(ForwardPointerVec.begin(), ForwardPointerVec.end(), Comp) != ForwardPointerVec.end()) continue; visit(Op); } State = Visited; Op OC = E->getOpCode(); if (OC == OpTypeInt) TypeIntVec.push_back(static_cast<SPIRVType*>(E)); else if (isConstantOpCode(OC)) { SPIRVConstant *C = static_cast<SPIRVConstant*>(E); if (C->getType()->isTypeInt()) ConstIntVec.push_back(C); else ConstVec.push_back(C); } else if (isTypeOpCode(OC)) TypeVec.push_back(static_cast<SPIRVType*>(E)); else if (E->isVariable()) VariableVec.push_back(static_cast<SPIRVVariable*>(E)); } public: TopologicalSort(const SPIRVTypeVec &_TypeVec, const SPIRVConstantVector &_ConstVec, const SPIRVVariableVec &_VariableVec, const SPIRVForwardPointerVec &_ForwardPointerVec) : ForwardPointerVec(_ForwardPointerVec), EntryStateMap([](SPIRVEntry* a, SPIRVEntry* b) -> bool { return a->getId() < b->getId(); }) { // Collect entries for sorting for (auto *T : _TypeVec) EntryStateMap[T] = DFSState::Unvisited; for (auto *C : _ConstVec) EntryStateMap[C] = DFSState::Unvisited; for (auto *V : _VariableVec) EntryStateMap[V] = DFSState::Unvisited; // Run topoligical sort for (auto ES : EntryStateMap) visit(ES.first); } }; spv_ostream & operator<< (spv_ostream &O, const TopologicalSort &S) { O << S.TypeIntVec << S.ConstIntVec << S.TypeVec << S.ConstVec << S.VariableVec; return O; } spv_ostream & operator<< (spv_ostream &O, SPIRVModule &M) { SPIRVModuleImpl &MI = *static_cast<SPIRVModuleImpl*>(&M); SPIRVEncoder Encoder(O); Encoder << MagicNumber << MI.SPIRVVersion << (((SPIRVWord)MI.GeneratorId << 16) | MI.GeneratorVer) << MI.NextId /* Bound for Id */ << MI.InstSchema; O << SPIRVNL(); for (auto &I:MI.CapMap) O << *I.second; for (auto &I:M.getExtension()) { assert(!I.empty() && "Invalid extension"); O << SPIRVExtension(&M, I); } for (auto &I:MI.IdBuiltinMap) O << SPIRVExtInstImport(&M, I.first, SPIRVBuiltinSetNameMap::map(I.second)); O << SPIRVMemoryModel(&M); for (auto &I:MI.EntryPointVec) for (auto &II:I.second) O << SPIRVEntryPoint(&M, I.first, II, M.get<SPIRVFunction>(II)->getName()); for (auto &I:MI.EntryPointVec) for (auto &II:I.second) MI.get<SPIRVFunction>(II)->encodeExecutionModes(O); O << MI.StringVec; for (auto &I:M.getSourceExtension()) { assert(!I.empty() && "Invalid source extension"); O << SPIRVSourceExtension(&M, I); } O << SPIRVSource(&M); for (auto &I:MI.NamedId) { // Don't output name for entry point since it is redundant bool IsEntryPoint = false; for (auto &EPS:MI.EntryPointSet) if (EPS.second.count(I)) { IsEntryPoint = true; break; } if (!IsEntryPoint) M.getEntry(I)->encodeName(O); } O << MI.MemberNameVec << MI.LineVec << MI.DecGroupVec << MI.DecorateSet << MI.GroupDecVec << MI.ForwardPointerVec << TopologicalSort(MI.TypeVec, MI.ConstVec, MI.VariableVec, MI.ForwardPointerVec) << SPIRVNL() << MI.FuncVec; return O; } template<class T> void SPIRVModuleImpl::addTo(std::vector<T*>& V, SPIRVEntry* E) { V.push_back(static_cast<T *>(E)); } // The first decoration group includes all the previously defined decorates. // The second decoration group includes all the decorates defined between the // first and second decoration group. So long so forth. SPIRVDecorationGroup* SPIRVModuleImpl::addDecorationGroup() { return addDecorationGroup(new SPIRVDecorationGroup(this, getId())); } SPIRVDecorationGroup* SPIRVModuleImpl::addDecorationGroup(SPIRVDecorationGroup* Group) { add(Group); Group->takeDecorates(DecorateSet); DecGroupVec.push_back(Group); SPIRVDBG(spvdbgs() << "[addDecorationGroup] {" << *Group << "}\n"; spvdbgs() << " Remaining DecorateSet: {" << DecorateSet << "}\n"); assert(DecorateSet.empty()); return Group; } SPIRVGroupDecorateGeneric* SPIRVModuleImpl::addGroupDecorateGeneric(SPIRVGroupDecorateGeneric *GDec) { add(GDec); GDec->decorateTargets(); GroupDecVec.push_back(GDec); return GDec; } SPIRVGroupDecorate* SPIRVModuleImpl::addGroupDecorate( SPIRVDecorationGroup* Group, const std::vector<SPIRVEntry*>& Targets) { auto GD = new SPIRVGroupDecorate(Group, getIds(Targets)); addGroupDecorateGeneric(GD); return GD; } SPIRVGroupMemberDecorate* SPIRVModuleImpl::addGroupMemberDecorate( SPIRVDecorationGroup* Group, const std::vector<SPIRVEntry*>& Targets) { auto GMD = new SPIRVGroupMemberDecorate(Group, getIds(Targets)); addGroupDecorateGeneric(GMD); return GMD; } SPIRVString* SPIRVModuleImpl::getString(const std::string& Str) { auto Loc = StrMap.find(Str); if (Loc != StrMap.end()) return Loc->second; auto S = add(new SPIRVString(this, getId(), Str)); StrMap[Str] = S; return S; } SPIRVMemberName* SPIRVModuleImpl::addMemberName(SPIRVTypeStruct* ST, SPIRVWord MemberNumber, const std::string& Name) { return add(new SPIRVMemberName(ST, MemberNumber, Name)); } void SPIRVModuleImpl::addUnknownStructField(SPIRVTypeStruct *Struct, unsigned I, SPIRVId ID) { UnknownStructFieldMap[Struct].push_back(std::make_pair(I, ID)); } std::istream & operator>> (std::istream &I, SPIRVModule &M) { SPIRVDecoder Decoder(I, M); SPIRVModuleImpl &MI = *static_cast<SPIRVModuleImpl*>(&M); // Disable automatic capability filling. MI.setAutoAddCapability(false); SPIRVWord Magic; Decoder >> Magic; assert(Magic == MagicNumber && "Invalid magic number"); Decoder >> MI.SPIRVVersion; assert(MI.SPIRVVersion <= SPV_VERSION && "Unsupported SPIRV version number"); SPIRVWord Generator = 0; Decoder >> Generator; MI.GeneratorId = Generator >> 16; MI.GeneratorVer = Generator & 0xFFFF; // Bound for Id Decoder >> MI.NextId; Decoder >> MI.InstSchema; assert(MI.InstSchema == SPIRVISCH_Default && "Unsupported instruction schema"); while(Decoder.getWordCountAndOpCode()) Decoder.getEntry(); MI.optimizeDecorates(); MI.resolveUnknownStructFields(); MI.createForwardPointers(); return I; } SPIRVModule * SPIRVModule::createSPIRVModule() { return new SPIRVModuleImpl; } SPIRVValue * SPIRVModuleImpl::getValue(SPIRVId TheId)const { return get<SPIRVValue>(TheId); } SPIRVType * SPIRVModuleImpl::getValueType(SPIRVId TheId)const { return get<SPIRVValue>(TheId)->getType(); } std::vector<SPIRVValue *> SPIRVModuleImpl::getValues(const std::vector<SPIRVId>& IdVec)const { std::vector<SPIRVValue *> ValueVec; for (auto i:IdVec) ValueVec.push_back(getValue(i)); return ValueVec; } std::vector<SPIRVType *> SPIRVModuleImpl::getValueTypes(const std::vector<SPIRVId>& IdVec)const { std::vector<SPIRVType *> TypeVec; for (auto i:IdVec) TypeVec.push_back(getValue(i)->getType()); return TypeVec; } std::vector<SPIRVId> SPIRVModuleImpl::getIds(const std::vector<SPIRVEntry *> &ValueVec)const { std::vector<SPIRVId> IdVec; for (auto i:ValueVec) IdVec.push_back(i->getId()); return IdVec; } std::vector<SPIRVId> SPIRVModuleImpl::getIds(const std::vector<SPIRVValue *> &ValueVec)const { std::vector<SPIRVId> IdVec; for (auto i:ValueVec) IdVec.push_back(i->getId()); return IdVec; } SPIRVInstTemplateBase* SPIRVModuleImpl::addInstTemplate(Op OC, SPIRVBasicBlock* BB, SPIRVType *Ty) { assert (!Ty || !Ty->isTypeVoid()); SPIRVId Id = Ty ? getId() : SPIRVID_INVALID; auto Ins = SPIRVInstTemplateBase::create(OC, Ty, Id, BB, this); BB->addInstruction(Ins); return Ins; } SPIRVInstTemplateBase* SPIRVModuleImpl::addInstTemplate(Op OC, const std::vector<SPIRVWord>& Ops, SPIRVBasicBlock* BB, SPIRVType *Ty) { assert (!Ty || !Ty->isTypeVoid()); SPIRVId Id = Ty ? getId() : SPIRVID_INVALID; auto Ins = SPIRVInstTemplateBase::create(OC, Ty, Id, Ops, BB, this); BB->addInstruction(Ins); return Ins; } SPIRVDbgInfo::SPIRVDbgInfo(SPIRVModule *TM) :M(TM){ } std::string SPIRVDbgInfo::getEntryPointFileStr(SPIRVExecutionModelKind EM, unsigned I) { if (M->getNumEntryPoints(EM) == 0) return ""; return getFunctionFileStr(M->getEntryPoint(EM, I)); } std::string SPIRVDbgInfo::getFunctionFileStr(SPIRVFunction *F) { if (F->hasLine()) return F->getLine()->getFileNameStr(); return ""; } unsigned SPIRVDbgInfo::getFunctionLineNo(SPIRVFunction *F) { if (F->hasLine()) return F->getLine()->getLine(); return 0; } bool IsSPIRVBinary(const std::string &Img) { if (Img.size() < sizeof(unsigned)) return false; auto Magic = reinterpret_cast<const unsigned*>(Img.data()); return *Magic == MagicNumber; } #ifdef _SPIRV_SUPPORT_TEXT_FMT bool ConvertSPIRV(std::istream &IS, spv_ostream &OS, std::string &ErrMsg, bool FromText, bool ToText) { auto SaveOpt = SPIRVUseTextFormat; SPIRVUseTextFormat = FromText; SPIRVModuleImpl M; IS >> M; if (M.getError(ErrMsg) != SPIRVEC_Success) { SPIRVUseTextFormat = SaveOpt; return false; } SPIRVUseTextFormat = ToText; OS << M; if (M.getError(ErrMsg) != SPIRVEC_Success) { SPIRVUseTextFormat = SaveOpt; return false; } SPIRVUseTextFormat = SaveOpt; return true; } bool IsSPIRVText(const std::string &Img) { std::istringstream SS(Img); unsigned Magic = 0; SS >> Magic; if (SS.bad()) return false; return Magic == MagicNumber; } bool ConvertSPIRV(std::string &Input, std::string &Out, std::string &ErrMsg, bool ToText) { auto FromText = IsSPIRVText(Input); if (ToText == FromText) { Out = Input; return true; } std::istringstream IS(Input); #ifdef _SPIRV_LLVM_API llvm::raw_string_ostream OS(Out); #else std::ostringstream OS; #endif if (!ConvertSPIRV(IS, OS, ErrMsg, FromText, ToText)) return false; Out = OS.str(); return true; } #endif // _SPIRV_SUPPORT_TEXT_FMT }