| // Copyright (c) 2016 Google Inc. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // This file provides a class hierarchy for representing SPIR-V types. | |
| #ifndef SOURCE_OPT_TYPES_H_ | |
| #define SOURCE_OPT_TYPES_H_ | |
| #include <map> | |
| #include <memory> | |
| #include <set> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include "source/latest_version_spirv_header.h" | |
| #include "source/opt/instruction.h" | |
| #include "source/util/small_vector.h" | |
| #include "spirv-tools/libspirv.h" | |
| namespace spvtools { | |
| namespace opt { | |
| namespace analysis { | |
| class Void; | |
| class Bool; | |
| class Integer; | |
| class Float; | |
| class Vector; | |
| class Matrix; | |
| class Image; | |
| class Sampler; | |
| class SampledImage; | |
| class Array; | |
| class RuntimeArray; | |
| class Struct; | |
| class Opaque; | |
| class Pointer; | |
| class Function; | |
| class Event; | |
| class DeviceEvent; | |
| class ReserveId; | |
| class Queue; | |
| class Pipe; | |
| class ForwardPointer; | |
| class PipeStorage; | |
| class NamedBarrier; | |
| class AccelerationStructureNV; | |
| class CooperativeMatrixNV; | |
| class RayQueryKHR; | |
| class HitObjectNV; | |
| // Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods, | |
| // which is used as a way to probe the actual <subclass>. | |
| class Type { | |
| public: | |
| typedef std::set<std::pair<const Pointer*, const Pointer*>> IsSameCache; | |
| using SeenTypes = spvtools::utils::SmallVector<const Type*, 8>; | |
| // Available subtypes. | |
| // | |
| // When adding a new derived class of Type, please add an entry to the enum. | |
| enum Kind { | |
| kVoid, | |
| kBool, | |
| kInteger, | |
| kFloat, | |
| kVector, | |
| kMatrix, | |
| kImage, | |
| kSampler, | |
| kSampledImage, | |
| kArray, | |
| kRuntimeArray, | |
| kStruct, | |
| kOpaque, | |
| kPointer, | |
| kFunction, | |
| kEvent, | |
| kDeviceEvent, | |
| kReserveId, | |
| kQueue, | |
| kPipe, | |
| kForwardPointer, | |
| kPipeStorage, | |
| kNamedBarrier, | |
| kAccelerationStructureNV, | |
| kCooperativeMatrixNV, | |
| kRayQueryKHR, | |
| kHitObjectNV, | |
| kLast | |
| }; | |
| Type(Kind k) : kind_(k) {} | |
| virtual ~Type() = default; | |
| // Attaches a decoration directly on this type. | |
| void AddDecoration(std::vector<uint32_t>&& d) { | |
| decorations_.push_back(std::move(d)); | |
| } | |
| // Returns the decorations on this type as a string. | |
| std::string GetDecorationStr() const; | |
| // Returns true if this type has exactly the same decorations as |that| type. | |
| bool HasSameDecorations(const Type* that) const; | |
| // Returns true if this type is exactly the same as |that| type, including | |
| // decorations. | |
| bool IsSame(const Type* that) const { | |
| IsSameCache seen; | |
| return IsSameImpl(that, &seen); | |
| } | |
| // Returns true if this type is exactly the same as |that| type, including | |
| // decorations. |seen| is the set of |Pointer*| pair that are currently being | |
| // compared in a parent call to |IsSameImpl|. | |
| virtual bool IsSameImpl(const Type* that, IsSameCache* seen) const = 0; | |
| // Returns a human-readable string to represent this type. | |
| virtual std::string str() const = 0; | |
| Kind kind() const { return kind_; } | |
| const std::vector<std::vector<uint32_t>>& decorations() const { | |
| return decorations_; | |
| } | |
| // Returns true if there is no decoration on this type. For struct types, | |
| // returns true only when there is no decoration for both the struct type | |
| // and the struct members. | |
| virtual bool decoration_empty() const { return decorations_.empty(); } | |
| // Creates a clone of |this|. | |
| std::unique_ptr<Type> Clone() const; | |
| // Returns a clone of |this| minus any decorations. | |
| std::unique_ptr<Type> RemoveDecorations() const; | |
| // Returns true if this cannot hash to the same value as another type in the | |
| // module. For example, structs are not unique types because the module could | |
| // have two types | |
| // | |
| // %1 = OpTypeStruct %int | |
| // %2 = OpTypeStruct %int | |
| // | |
| // The only way to distinguish these types is the result id. The type manager | |
| // will hash them to the same value. | |
| bool IsUniqueType() const; | |
| bool operator==(const Type& other) const; | |
| // Returns the hash value of this type. | |
| size_t HashValue() const; | |
| size_t ComputeHashValue(size_t hash, SeenTypes* seen) const; | |
| // Returns the number of components in a composite type. Returns 0 for a | |
| // non-composite type. | |
| uint64_t NumberOfComponents() const; | |
| // A bunch of methods for casting this type to a given type. Returns this if the | |
| // cast can be done, nullptr otherwise. | |
| // clang-format off | |
| #define DeclareCastMethod(target) \ | |
| virtual target* As##target() { return nullptr; } \ | |
| virtual const target* As##target() const { return nullptr; } | |
| DeclareCastMethod(Void) | |
| DeclareCastMethod(Bool) | |
| DeclareCastMethod(Integer) | |
| DeclareCastMethod(Float) | |
| DeclareCastMethod(Vector) | |
| DeclareCastMethod(Matrix) | |
| DeclareCastMethod(Image) | |
| DeclareCastMethod(Sampler) | |
| DeclareCastMethod(SampledImage) | |
| DeclareCastMethod(Array) | |
| DeclareCastMethod(RuntimeArray) | |
| DeclareCastMethod(Struct) | |
| DeclareCastMethod(Opaque) | |
| DeclareCastMethod(Pointer) | |
| DeclareCastMethod(Function) | |
| DeclareCastMethod(Event) | |
| DeclareCastMethod(DeviceEvent) | |
| DeclareCastMethod(ReserveId) | |
| DeclareCastMethod(Queue) | |
| DeclareCastMethod(Pipe) | |
| DeclareCastMethod(ForwardPointer) | |
| DeclareCastMethod(PipeStorage) | |
| DeclareCastMethod(NamedBarrier) | |
| DeclareCastMethod(AccelerationStructureNV) | |
| DeclareCastMethod(CooperativeMatrixNV) | |
| DeclareCastMethod(RayQueryKHR) | |
| DeclareCastMethod(HitObjectNV) | |
| #undef DeclareCastMethod | |
| protected: | |
| // Add any type-specific state to |hash| and returns new hash. | |
| virtual size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const = 0; | |
| protected: | |
| // Decorations attached to this type. Each decoration is encoded as a vector | |
| // of uint32_t numbers. The first uint32_t number is the decoration value, | |
| // and the rest are the parameters to the decoration (if exists). | |
| std::vector<std::vector<uint32_t>> decorations_; | |
| private: | |
| // Removes decorations on this type. For struct types, also removes element | |
| // decorations. | |
| virtual void ClearDecorations() { decorations_.clear(); } | |
| Kind kind_; | |
| }; | |
| // clang-format on | |
| class Integer : public Type { | |
| public: | |
| Integer(uint32_t w, bool is_signed) | |
| : Type(kInteger), width_(w), signed_(is_signed) {} | |
| Integer(const Integer&) = default; | |
| std::string str() const override; | |
| Integer* AsInteger() override { return this; } | |
| const Integer* AsInteger() const override { return this; } | |
| uint32_t width() const { return width_; } | |
| bool IsSigned() const { return signed_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| uint32_t width_; // bit width | |
| bool signed_; // true if this integer is signed | |
| }; | |
| class Float : public Type { | |
| public: | |
| Float(uint32_t w) : Type(kFloat), width_(w) {} | |
| Float(const Float&) = default; | |
| std::string str() const override; | |
| Float* AsFloat() override { return this; } | |
| const Float* AsFloat() const override { return this; } | |
| uint32_t width() const { return width_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| uint32_t width_; // bit width | |
| }; | |
| class Vector : public Type { | |
| public: | |
| Vector(const Type* element_type, uint32_t count); | |
| Vector(const Vector&) = default; | |
| std::string str() const override; | |
| const Type* element_type() const { return element_type_; } | |
| uint32_t element_count() const { return count_; } | |
| Vector* AsVector() override { return this; } | |
| const Vector* AsVector() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* element_type_; | |
| uint32_t count_; | |
| }; | |
| class Matrix : public Type { | |
| public: | |
| Matrix(const Type* element_type, uint32_t count); | |
| Matrix(const Matrix&) = default; | |
| std::string str() const override; | |
| const Type* element_type() const { return element_type_; } | |
| uint32_t element_count() const { return count_; } | |
| Matrix* AsMatrix() override { return this; } | |
| const Matrix* AsMatrix() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* element_type_; | |
| uint32_t count_; | |
| }; | |
| class Image : public Type { | |
| public: | |
| Image(Type* type, spv::Dim dimen, uint32_t d, bool array, bool multisample, | |
| uint32_t sampling, spv::ImageFormat f, | |
| spv::AccessQualifier qualifier = spv::AccessQualifier::ReadOnly); | |
| Image(const Image&) = default; | |
| std::string str() const override; | |
| Image* AsImage() override { return this; } | |
| const Image* AsImage() const override { return this; } | |
| const Type* sampled_type() const { return sampled_type_; } | |
| spv::Dim dim() const { return dim_; } | |
| uint32_t depth() const { return depth_; } | |
| bool is_arrayed() const { return arrayed_; } | |
| bool is_multisampled() const { return ms_; } | |
| uint32_t sampled() const { return sampled_; } | |
| spv::ImageFormat format() const { return format_; } | |
| spv::AccessQualifier access_qualifier() const { return access_qualifier_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| Type* sampled_type_; | |
| spv::Dim dim_; | |
| uint32_t depth_; | |
| bool arrayed_; | |
| bool ms_; | |
| uint32_t sampled_; | |
| spv::ImageFormat format_; | |
| spv::AccessQualifier access_qualifier_; | |
| }; | |
| class SampledImage : public Type { | |
| public: | |
| SampledImage(Type* image) : Type(kSampledImage), image_type_(image) {} | |
| SampledImage(const SampledImage&) = default; | |
| std::string str() const override; | |
| SampledImage* AsSampledImage() override { return this; } | |
| const SampledImage* AsSampledImage() const override { return this; } | |
| const Type* image_type() const { return image_type_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| Type* image_type_; | |
| }; | |
| class Array : public Type { | |
| public: | |
| // Data about the length operand, that helps us distinguish between one | |
| // array length and another. | |
| struct LengthInfo { | |
| // The result id of the instruction defining the length. | |
| const uint32_t id; | |
| enum Case : uint32_t { | |
| kConstant = 0, | |
| kConstantWithSpecId = 1, | |
| kDefiningId = 2 | |
| }; | |
| // Extra words used to distinshish one array length and another. | |
| // - if OpConstant, then it's 0, then the words in the literal constant | |
| // value. | |
| // - if OpSpecConstant, then it's 1, then the SpecID decoration if there | |
| // is one, followed by the words in the literal constant value. | |
| // The spec might not be overridden, in which case we'll end up using | |
| // the literal value. | |
| // - Otherwise, it's an OpSpecConsant, and this 2, then the ID (again). | |
| const std::vector<uint32_t> words; | |
| }; | |
| // Constructs an array type with given element and length. If the length | |
| // is an OpSpecConstant, then |spec_id| should be its SpecId decoration. | |
| Array(const Type* element_type, const LengthInfo& length_info_arg); | |
| Array(const Array&) = default; | |
| std::string str() const override; | |
| const Type* element_type() const { return element_type_; } | |
| uint32_t LengthId() const { return length_info_.id; } | |
| const LengthInfo& length_info() const { return length_info_; } | |
| Array* AsArray() override { return this; } | |
| const Array* AsArray() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| void ReplaceElementType(const Type* element_type); | |
| LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* element_type_; | |
| const LengthInfo length_info_; | |
| }; | |
| class RuntimeArray : public Type { | |
| public: | |
| RuntimeArray(const Type* element_type); | |
| RuntimeArray(const RuntimeArray&) = default; | |
| std::string str() const override; | |
| const Type* element_type() const { return element_type_; } | |
| RuntimeArray* AsRuntimeArray() override { return this; } | |
| const RuntimeArray* AsRuntimeArray() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| void ReplaceElementType(const Type* element_type); | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* element_type_; | |
| }; | |
| class Struct : public Type { | |
| public: | |
| Struct(const std::vector<const Type*>& element_types); | |
| Struct(const Struct&) = default; | |
| // Adds a decoration to the member at the given index. The first word is the | |
| // decoration enum, and the remaining words, if any, are its operands. | |
| void AddMemberDecoration(uint32_t index, std::vector<uint32_t>&& decoration); | |
| std::string str() const override; | |
| const std::vector<const Type*>& element_types() const { | |
| return element_types_; | |
| } | |
| std::vector<const Type*>& element_types() { return element_types_; } | |
| bool decoration_empty() const override { | |
| return decorations_.empty() && element_decorations_.empty(); | |
| } | |
| const std::map<uint32_t, std::vector<std::vector<uint32_t>>>& | |
| element_decorations() const { | |
| return element_decorations_; | |
| } | |
| Struct* AsStruct() override { return this; } | |
| const Struct* AsStruct() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| void ClearDecorations() override { | |
| decorations_.clear(); | |
| element_decorations_.clear(); | |
| } | |
| std::vector<const Type*> element_types_; | |
| // We can attach decorations to struct members and that should not affect the | |
| // underlying element type. So we need an extra data structure here to keep | |
| // track of element type decorations. They must be stored in an ordered map | |
| // because |GetExtraHashWords| will traverse the structure. It must have a | |
| // fixed order in order to hash to the same value every time. | |
| std::map<uint32_t, std::vector<std::vector<uint32_t>>> element_decorations_; | |
| }; | |
| class Opaque : public Type { | |
| public: | |
| Opaque(std::string n) : Type(kOpaque), name_(std::move(n)) {} | |
| Opaque(const Opaque&) = default; | |
| std::string str() const override; | |
| Opaque* AsOpaque() override { return this; } | |
| const Opaque* AsOpaque() const override { return this; } | |
| const std::string& name() const { return name_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| std::string name_; | |
| }; | |
| class Pointer : public Type { | |
| public: | |
| Pointer(const Type* pointee, spv::StorageClass sc); | |
| Pointer(const Pointer&) = default; | |
| std::string str() const override; | |
| const Type* pointee_type() const { return pointee_type_; } | |
| spv::StorageClass storage_class() const { return storage_class_; } | |
| Pointer* AsPointer() override { return this; } | |
| const Pointer* AsPointer() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| void SetPointeeType(const Type* type); | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* pointee_type_; | |
| spv::StorageClass storage_class_; | |
| }; | |
| class Function : public Type { | |
| public: | |
| Function(const Type* ret_type, const std::vector<const Type*>& params); | |
| Function(const Type* ret_type, std::vector<const Type*>& params); | |
| Function(const Function&) = default; | |
| std::string str() const override; | |
| Function* AsFunction() override { return this; } | |
| const Function* AsFunction() const override { return this; } | |
| const Type* return_type() const { return return_type_; } | |
| const std::vector<const Type*>& param_types() const { return param_types_; } | |
| std::vector<const Type*>& param_types() { return param_types_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| void SetReturnType(const Type* type); | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* return_type_; | |
| std::vector<const Type*> param_types_; | |
| }; | |
| class Pipe : public Type { | |
| public: | |
| Pipe(spv::AccessQualifier qualifier) | |
| : Type(kPipe), access_qualifier_(qualifier) {} | |
| Pipe(const Pipe&) = default; | |
| std::string str() const override; | |
| Pipe* AsPipe() override { return this; } | |
| const Pipe* AsPipe() const override { return this; } | |
| spv::AccessQualifier access_qualifier() const { return access_qualifier_; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| spv::AccessQualifier access_qualifier_; | |
| }; | |
| class ForwardPointer : public Type { | |
| public: | |
| ForwardPointer(uint32_t id, spv::StorageClass sc) | |
| : Type(kForwardPointer), | |
| target_id_(id), | |
| storage_class_(sc), | |
| pointer_(nullptr) {} | |
| ForwardPointer(const ForwardPointer&) = default; | |
| uint32_t target_id() const { return target_id_; } | |
| void SetTargetPointer(const Pointer* pointer) { pointer_ = pointer; } | |
| spv::StorageClass storage_class() const { return storage_class_; } | |
| const Pointer* target_pointer() const { return pointer_; } | |
| std::string str() const override; | |
| ForwardPointer* AsForwardPointer() override { return this; } | |
| const ForwardPointer* AsForwardPointer() const override { return this; } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| uint32_t target_id_; | |
| spv::StorageClass storage_class_; | |
| const Pointer* pointer_; | |
| }; | |
| class CooperativeMatrixNV : public Type { | |
| public: | |
| CooperativeMatrixNV(const Type* type, const uint32_t scope, | |
| const uint32_t rows, const uint32_t columns); | |
| CooperativeMatrixNV(const CooperativeMatrixNV&) = default; | |
| std::string str() const override; | |
| CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; } | |
| const CooperativeMatrixNV* AsCooperativeMatrixNV() const override { | |
| return this; | |
| } | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; | |
| const Type* component_type() const { return component_type_; } | |
| uint32_t scope_id() const { return scope_id_; } | |
| uint32_t rows_id() const { return rows_id_; } | |
| uint32_t columns_id() const { return columns_id_; } | |
| private: | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override; | |
| const Type* component_type_; | |
| const uint32_t scope_id_; | |
| const uint32_t rows_id_; | |
| const uint32_t columns_id_; | |
| }; | |
| #define DefineParameterlessType(type, name) \ | |
| class type : public Type { \ | |
| public: \ | |
| type() : Type(k##type) {} \ | |
| type(const type&) = default; \ | |
| \ | |
| std::string str() const override { return #name; } \ | |
| \ | |
| type* As##type() override { return this; } \ | |
| const type* As##type() const override { return this; } \ | |
| \ | |
| size_t ComputeExtraStateHash(size_t hash, SeenTypes*) const override { \ | |
| return hash; \ | |
| } \ | |
| \ | |
| private: \ | |
| bool IsSameImpl(const Type* that, IsSameCache*) const override { \ | |
| return that->As##type() && HasSameDecorations(that); \ | |
| } \ | |
| } | |
| DefineParameterlessType(Void, void); | |
| DefineParameterlessType(Bool, bool); | |
| DefineParameterlessType(Sampler, sampler); | |
| DefineParameterlessType(Event, event); | |
| DefineParameterlessType(DeviceEvent, device_event); | |
| DefineParameterlessType(ReserveId, reserve_id); | |
| DefineParameterlessType(Queue, queue); | |
| DefineParameterlessType(PipeStorage, pipe_storage); | |
| DefineParameterlessType(NamedBarrier, named_barrier); | |
| DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV); | |
| DefineParameterlessType(RayQueryKHR, rayQueryKHR); | |
| DefineParameterlessType(HitObjectNV, hitObjectNV); | |
| #undef DefineParameterlessType | |
| } // namespace analysis | |
| } // namespace opt | |
| } // namespace spvtools | |
| #endif // SOURCE_OPT_TYPES_H_ |