//===- SPIRVValue.h - Class to represent a SPIR-V Value ---------*- C++ -*-===// // // The LLVM/SPIRV Translator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // // Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal with the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimers in the documentation // and/or other materials provided with the distribution. // Neither the names of Advanced Micro Devices, Inc., nor the names of its // contributors may be used to endorse or promote products derived from this // Software without specific prior written permission. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // //===----------------------------------------------------------------------===// /// \file /// /// This file defines the values defined in SPIR-V spec with op codes. /// /// The name of the SPIR-V values follow the op code name in the spec. /// This is for readability and ease of using macro to handle types. // //===----------------------------------------------------------------------===// #ifndef SPIRV_LIBSPIRV_SPIRVVALUE_H #define SPIRV_LIBSPIRV_SPIRVVALUE_H #include "SPIRVDecorate.h" #include "SPIRVEntry.h" #include "SPIRVType.h" namespace llvm { class APInt; } // namespace llvm #include namespace SPIRV { class SPIRVValue : public SPIRVEntry { public: // Complete constructor for value with id and type SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, SPIRVType *TheType, SPIRVId TheId) : SPIRVEntry(M, TheWordCount, TheOpCode, TheId), Type(TheType) { validate(); } // Complete constructor for value with type but without id SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, SPIRVType *TheType) : SPIRVEntry(M, TheWordCount, TheOpCode), Type(TheType) { setHasNoId(); SPIRVValue::validate(); } // Complete constructor for value with id but without type SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, SPIRVId TheId) : SPIRVEntry(M, TheWordCount, TheOpCode, TheId), Type(NULL) { setHasNoType(); SPIRVValue::validate(); } // Complete constructor for value without id and type SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode) : SPIRVEntry(M, TheWordCount, TheOpCode), Type(NULL) { setHasNoId(); setHasNoType(); SPIRVValue::validate(); } // Incomplete constructor SPIRVValue(Op TheOpCode) : SPIRVEntry(TheOpCode), Type(NULL) {} bool hasType() const { return !(Attrib & SPIRVEA_NOTYPE); } SPIRVType *getType() const { assert(hasType() && "value has no type"); return Type; } bool isVolatile() const; bool hasAlignment(SPIRVWord *Result = 0) const; bool hasNoSignedWrap() const; bool hasNoUnsignedWrap() const; void setAlignment(SPIRVWord); void setVolatile(bool IsVolatile); template void setNoIntegerDecorationWrap(bool HasNoIntegerWrap); void setFPFastMathMode(SPIRVWord FPFastMathMode); void validate() const override { SPIRVEntry::validate(); assert((!hasType() || Type) && "Invalid type"); } void setType(SPIRVType *Ty) { Type = Ty; assert(!Ty || !Ty->isTypeVoid() || OpCode == OpFunction || OpCode == internal::OpTaskSequenceGetINTEL); if (Ty && (!Ty->isTypeVoid() || OpCode == OpFunction || OpCode == internal::OpTaskSequenceGetINTEL)) setHasType(); else setHasNoType(); } SPIRVCapVec getRequiredCapability() const override { SPIRVCapVec CV; if (!hasType()) return CV; return Type->getRequiredCapability(); } std::optional getRequiredExtension() const override { std::optional EV; if (!hasType()) return EV; EV = Type->getRequiredExtension(); assert(Module && (!EV.has_value() || Module->isAllowedToUseExtension(EV.value()))); return EV; } protected: void setHasNoType() { Attrib |= SPIRVEA_NOTYPE; } void setHasType() { Attrib &= ~SPIRVEA_NOTYPE; } SPIRVType *Type; // Value Type }; template class SPIRVConstantBase : public SPIRVValue { public: // Complete constructor for integer constant SPIRVConstantBase(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, uint64_t TheValue) : SPIRVValue(M, 0, OC, TheType, TheId) { setWords(&TheValue); } // Incomplete constructor for AP integer constant SPIRVConstantBase(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, const llvm::APInt &TheValue); // Complete constructor for float constant SPIRVConstantBase(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, float TheValue) : SPIRVValue(M, 0, OC, TheType, TheId) { setWords(reinterpret_cast(&TheValue)); } // Complete constructor for double constant SPIRVConstantBase(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, double TheValue) : SPIRVValue(M, 0, OC, TheType, TheId) { setWords(reinterpret_cast(&TheValue)); } // Incomplete constructor SPIRVConstantBase() : SPIRVValue(OC), NumWords(0) {} uint64_t getZExtIntValue() const { return getValue(); } float getFloatValue() const { return getValue(); } double getDoubleValue() const { return getValue(); } unsigned getNumWords() const { return NumWords; } const std::vector &getSPIRVWords() { return Words; } protected: constexpr static SPIRVWord FixedWC = 3; // Common method for getting values of size less or equal to 64 bits. template T getValue() const { if (OpCode == OpConstantNull) { assert(std::is_arithmetic_v && "OpConstantNull has no defined value for this type"); return 0; } constexpr auto ValueSize = static_cast(sizeof(T)); assert((ValueSize <= 8) && "Incorrect result type of requested value"); T TheValue{}; unsigned CopyBytes = std::min(ValueSize, NumWords * SpirvWordSize); std::memcpy(&TheValue, Words.data(), CopyBytes); return TheValue; } void setWords(const uint64_t *TheValue); void recalculateWordCount() { NumWords = (Type->getBitWidth() + SpirvWordBitWidth - 1) / SpirvWordBitWidth; WordCount = FixedWC + NumWords; } void validate() const override { SPIRVValue::validate(); assert(NumWords >= 1 && "Invalid constant size"); } void encode(spv_ostream &O) const override { getEncoder(O) << Type << Id; for (const auto &Word : Words) getEncoder(O) << Word; } void setWordCount(SPIRVWord WordCount) override { SPIRVValue::setWordCount(WordCount); NumWords = WordCount - FixedWC; } void decode(std::istream &I) override { getDecoder(I) >> Type >> Id; Words.resize(NumWords); for (auto &Word : Words) getDecoder(I) >> Word; } unsigned NumWords; private: std::vector Words; }; using SPIRVConstant = SPIRVConstantBase; using SPIRVSpecConstant = SPIRVConstantBase; template class SPIRVConstantEmpty : public SPIRVValue { public: // Complete constructor SPIRVConstantEmpty(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) : SPIRVValue(M, 3, OC, TheType, TheId) { validate(); } // Incomplete constructor SPIRVConstantEmpty() : SPIRVValue(OC) {} protected: void validate() const override { SPIRVValue::validate(); } _SPIRV_DEF_ENCDEC2(Type, Id) }; template class SPIRVConstantBool : public SPIRVConstantEmpty { public: // Complete constructor SPIRVConstantBool(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) : SPIRVConstantEmpty(M, TheType, TheId) {} // Incomplete constructor SPIRVConstantBool() {} protected: void validate() const override { SPIRVConstantEmpty::validate(); assert(this->Type->isTypeBool() && "Invalid type"); } }; typedef SPIRVConstantBool SPIRVConstantTrue; typedef SPIRVConstantBool SPIRVConstantFalse; typedef SPIRVConstantBool SPIRVSpecConstantTrue; typedef SPIRVConstantBool SPIRVSpecConstantFalse; class SPIRVConstantNull : public SPIRVConstantEmpty { public: // Complete constructor SPIRVConstantNull(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) : SPIRVConstantEmpty(M, TheType, TheId) { validate(); } // Incomplete constructor SPIRVConstantNull() {} protected: void validate() const override { SPIRVConstantEmpty::validate(); assert((Type->isTypeBool() || Type->isTypeInt() || Type->isTypeFloat() || Type->isTypeComposite() || Type->isTypeOpaque() || Type->isTypeEvent() || Type->isTypePointer() || Type->isTypeReserveId() || Type->isTypeDeviceEvent() || (Type->isTypeSubgroupAvcINTEL() && !Type->isTypeSubgroupAvcMceINTEL())) && "Invalid type"); } }; class SPIRVUndef : public SPIRVConstantEmpty { public: // Complete constructor SPIRVUndef(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) : SPIRVConstantEmpty(M, TheType, TheId) { validate(); } // Incomplete constructor SPIRVUndef() {} protected: void validate() const override { SPIRVConstantEmpty::validate(); } }; class SPIRVPoisonKHR : public SPIRVConstantEmpty { public: SPIRVPoisonKHR(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) : SPIRVConstantEmpty(M, TheType, TheId) { validate(); } SPIRVPoisonKHR() {} SPIRVCapVec getRequiredCapability() const override { return getVec(CapabilityPoisonFreezeKHR); } std::optional getRequiredExtension() const override { return ExtensionID::SPV_KHR_poison_freeze; } protected: void validate() const override { SPIRVConstantEmpty::validate(); } }; template class SPIRVConstantCompositeBase : public SPIRVValue { public: // There are always 3 words in this instruction except constituents: // 1) WordCount + OpCode // 2) Result type // 3) Result Id constexpr static SPIRVWord FixedWC = 3; using ContinuedInstType = typename InstToContinued::Type; // Complete constructor for composite constant SPIRVConstantCompositeBase(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, const std::vector TheElements) : SPIRVValue(M, TheElements.size() + FixedWC, OC, TheType, TheId) { Elements = getIds(TheElements); validate(); } // Incomplete constructor SPIRVConstantCompositeBase() : SPIRVValue(OC) {} std::vector getElements() const { return getValues(Elements); } // TODO: Should we attach operands of continued instructions as well? std::vector getNonLiteralOperands() const override { std::vector Elements = getElements(); return std::vector(Elements.begin(), Elements.end()); } std::vector getContinuedInstructions() { return ContinuedInstructions; } void addContinuedInstruction(ContinuedInstType Inst) { ContinuedInstructions.push_back(Inst); } void encodeChildren(spv_ostream &O) const override { O << SPIRVNL(); for (auto &I : ContinuedInstructions) O << *I; } protected: void validate() const override { SPIRVValue::validate(); for (auto &I : Elements) getValue(I)->validate(); } void setWordCount(SPIRVWord WordCount) override { SPIRVEntry::setWordCount(WordCount); SPIRVCK(WordCount >= FixedWC, InvalidWordCount, ""); Elements.resize(WordCount - FixedWC); } void encode(spv_ostream &O) const override { getEncoder(O) << Type << Id << Elements; } void decode(std::istream &I) override { SPIRVDecoder Decoder = getDecoder(I); Decoder >> Type >> Id >> Elements; for (SPIRVEntry *E : Decoder.getContinuedInstructions(ContinuedOpCode)) { addContinuedInstruction(static_cast(E)); } } std::vector Elements; std::vector ContinuedInstructions; const spv::Op ContinuedOpCode = InstToContinued::OpCode; }; using SPIRVConstantComposite = SPIRVConstantCompositeBase; using SPIRVSpecConstantComposite = SPIRVConstantCompositeBase; class SPIRVConstantSampler : public SPIRVValue { public: const static Op OC = OpConstantSampler; const static SPIRVWord WC = 6; // Complete constructor SPIRVConstantSampler(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, SPIRVWord TheAddrMode, SPIRVWord TheNormalized, SPIRVWord TheFilterMode) : SPIRVValue(M, WC, OC, TheType, TheId), AddrMode(TheAddrMode), Normalized(TheNormalized), FilterMode(TheFilterMode) { validate(); } // Incomplete constructor SPIRVConstantSampler() : SPIRVValue(OC), AddrMode(SPIRVSAM_Invalid), Normalized(SPIRVWORD_MAX), FilterMode(SPIRVSFM_Invalid) {} SPIRVWord getAddrMode() const { return AddrMode; } SPIRVWord getFilterMode() const { return FilterMode; } SPIRVWord getNormalized() const { return Normalized; } SPIRVCapVec getRequiredCapability() const override { return getVec(CapabilityLiteralSampler); } protected: SPIRVWord AddrMode; SPIRVWord Normalized; SPIRVWord FilterMode; void validate() const override { SPIRVValue::validate(); assert(OpCode == OC); assert(WordCount == WC); assert(Type->isTypeSampler()); } _SPIRV_DEF_ENCDEC5(Type, Id, AddrMode, Normalized, FilterMode) }; class SPIRVConstantPipeStorage : public SPIRVValue { public: const static Op OC = OpConstantPipeStorage; const static SPIRVWord WC = 6; // Complete constructor SPIRVConstantPipeStorage(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, SPIRVWord ThePacketSize, SPIRVWord ThePacketAlign, SPIRVWord TheCapacity) : SPIRVValue(M, WC, OC, TheType, TheId), PacketSize(ThePacketSize), PacketAlign(ThePacketAlign), Capacity(TheCapacity) { validate(); } // Incomplete constructor SPIRVConstantPipeStorage() : SPIRVValue(OC), PacketSize(0), PacketAlign(0), Capacity(0) {} SPIRVWord getPacketSize() const { return PacketSize; } SPIRVWord getPacketAlign() const { return PacketAlign; } SPIRVWord getCapacity() const { return Capacity; } SPIRVCapVec getRequiredCapability() const override { return getVec(CapabilityPipes, CapabilityPipeStorage); } protected: SPIRVWord PacketSize; SPIRVWord PacketAlign; SPIRVWord Capacity; void validate() const override { SPIRVValue::validate(); assert(OpCode == OC); assert(WordCount == WC); assert(Type->isTypePipeStorage()); } _SPIRV_DEF_ENCDEC5(Type, Id, PacketSize, PacketAlign, Capacity) }; class SPIRVForward : public SPIRVValue, public SPIRVComponentExecutionModes { public: const static Op OC = internal::OpForward; // Complete constructor SPIRVForward(SPIRVModule *TheModule, SPIRVType *TheTy, SPIRVId TheId) : SPIRVValue(TheModule, 0, OC, TheId) { if (TheTy) setType(TheTy); } SPIRVForward() : SPIRVValue(OC) { assert(0 && "should never be called"); } _SPIRV_DEF_ENCDEC1(Id) friend class SPIRVFunction; protected: void validate() const override {} }; } // namespace SPIRV #endif // SPIRV_LIBSPIRV_SPIRVVALUE_H