| // 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. |
| |
| #ifndef SOURCE_OPT_TYPE_MANAGER_H_ |
| #define SOURCE_OPT_TYPE_MANAGER_H_ |
| |
| #include <memory> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "source/opt/module.h" |
| #include "source/opt/types.h" |
| #include "spirv-tools/libspirv.hpp" |
| |
| namespace spvtools { |
| namespace opt { |
| |
| class IRContext; |
| |
| namespace analysis { |
| |
| // Hashing functor. |
| // |
| // All type pointers must be non-null. |
| struct HashTypePointer { |
| size_t operator()(const Type* type) const { |
| assert(type); |
| return type->HashValue(); |
| } |
| }; |
| struct HashTypeUniquePointer { |
| size_t operator()(const std::unique_ptr<Type>& type) const { |
| assert(type); |
| return type->HashValue(); |
| } |
| }; |
| |
| // Equality functor. |
| // |
| // Checks if two types pointers are the same type. |
| // |
| // All type pointers must be non-null. |
| struct CompareTypePointers { |
| bool operator()(const Type* lhs, const Type* rhs) const { |
| assert(lhs && rhs); |
| return lhs->IsSame(rhs); |
| } |
| }; |
| struct CompareTypeUniquePointers { |
| bool operator()(const std::unique_ptr<Type>& lhs, |
| const std::unique_ptr<Type>& rhs) const { |
| assert(lhs && rhs); |
| return lhs->IsSame(rhs.get()); |
| } |
| }; |
| |
| // A class for managing the SPIR-V type hierarchy. |
| class TypeManager { |
| public: |
| using IdToTypeMap = std::unordered_map<uint32_t, Type*>; |
| |
| // Constructs a type manager from the given |module|. All internal messages |
| // will be communicated to the outside via the given message |consumer|. |
| // This instance only keeps a reference to the |consumer|, so the |consumer| |
| // should outlive this instance. |
| TypeManager(const MessageConsumer& consumer, IRContext* c); |
| |
| TypeManager(const TypeManager&) = delete; |
| TypeManager(TypeManager&&) = delete; |
| TypeManager& operator=(const TypeManager&) = delete; |
| TypeManager& operator=(TypeManager&&) = delete; |
| |
| // Returns the type for the given type |id|. Returns nullptr if the given |id| |
| // does not define a type. |
| Type* GetType(uint32_t id) const; |
| // Returns the id for the given |type|. Returns 0 if can not find the given |
| // |type|. |
| uint32_t GetId(const Type* type) const; |
| // Returns the number of types hold in this manager. |
| size_t NumTypes() const { return id_to_type_.size(); } |
| // Iterators for all types contained in this manager. |
| IdToTypeMap::const_iterator begin() const { return id_to_type_.cbegin(); } |
| IdToTypeMap::const_iterator end() const { return id_to_type_.cend(); } |
| |
| // Returns a pair of the type and pointer to the type in |sc|. |
| // |
| // |id| must be a registered type. |
| std::pair<Type*, std::unique_ptr<Pointer>> GetTypeAndPointerType( |
| uint32_t id, spv::StorageClass sc) const; |
| |
| // Returns an id for a declaration representing |type|. Returns 0 if the type |
| // does not exists, and could not be generated. |
| // |
| // If |type| is registered, then the registered id is returned. Otherwise, |
| // this function recursively adds type and annotation instructions as |
| // necessary to fully define |type|. |
| uint32_t GetTypeInstruction(const Type* type); |
| |
| // Find pointer to type and storage in module, return its resultId. If it is |
| // not found, a new type is created, and its id is returned. Returns 0 if the |
| // type could not be created. |
| uint32_t FindPointerToType(uint32_t type_id, spv::StorageClass storage_class); |
| |
| // Registers |id| to |type|. |
| // |
| // If GetId(|type|) already returns a non-zero id, that mapping will be |
| // unchanged. |
| void RegisterType(uint32_t id, const Type& type); |
| |
| // Return the registered type object that is the same as |type|. |
| Type* GetRegisteredType(const Type* type); |
| |
| // Removes knowledge of |id| from the manager. |
| // |
| // If |id| is an ambiguous type the multiple ids may be registered to |id|'s |
| // type (e.g. %struct1 and %struct1 might hash to the same type). In that |
| // case, calling GetId() with |id|'s type will return another suitable id |
| // defining that type. |
| void RemoveId(uint32_t id); |
| |
| // Returns the type of the member of |parent_type| that is identified by |
| // |access_chain|. The vector |access_chain| is a series of integers that are |
| // used to pick members as in the |OpCompositeExtract| instructions. If you |
| // want a member of an array, vector, or matrix that does not have a constant |
| // index, you can use 0 in that position. All elements have the same type. |
| const Type* GetMemberType(const Type* parent_type, |
| const std::vector<uint32_t>& access_chain); |
| |
| // Attaches the decoration encoded in |inst| to |type|. Does nothing if the |
| // given instruction is not a decoration instruction. Assumes the target is |
| // |type| (e.g. should be called in loop of |type|'s decorations). |
| void AttachDecoration(const Instruction& inst, Type* type); |
| |
| Type* GetUIntType() { return GetIntType(32, false); } |
| |
| uint32_t GetUIntTypeId() { return GetTypeInstruction(GetUIntType()); } |
| |
| Type* GetIntType(int32_t bitWidth, bool isSigned) { |
| Integer int_type(bitWidth, isSigned); |
| return GetRegisteredType(&int_type); |
| } |
| |
| Type* GetSIntType() { return GetIntType(32, true); } |
| |
| uint32_t GetSIntTypeId() { return GetTypeInstruction(GetSIntType()); } |
| |
| Type* GetFloatType() { |
| Float float_type(32); |
| return GetRegisteredType(&float_type); |
| } |
| |
| uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); } |
| |
| Type* GetDoubleType() { |
| Float float_type(64); |
| return GetRegisteredType(&float_type); |
| } |
| |
| uint32_t GetDoubleTypeId() { return GetTypeInstruction(GetDoubleType()); } |
| |
| Type* GetUIntVectorType(uint32_t size) { |
| Vector vec_type(GetUIntType(), size); |
| return GetRegisteredType(&vec_type); |
| } |
| |
| uint32_t GetUIntVectorTypeId(uint32_t size) { |
| return GetTypeInstruction(GetUIntVectorType(size)); |
| } |
| |
| Type* GetSIntVectorType(uint32_t size) { |
| Vector vec_type(GetSIntType(), size); |
| return GetRegisteredType(&vec_type); |
| } |
| |
| uint32_t GetSIntVectorTypeId(uint32_t size) { |
| return GetTypeInstruction(GetSIntVectorType(size)); |
| } |
| |
| Type* GetFloatVectorType(uint32_t size) { |
| Vector vec_type(GetFloatType(), size); |
| return GetRegisteredType(&vec_type); |
| } |
| |
| uint32_t GetFloatVectorTypeId(uint32_t size) { |
| return GetTypeInstruction(GetFloatVectorType(size)); |
| } |
| |
| Type* GetBoolType() { |
| Bool bool_type; |
| return GetRegisteredType(&bool_type); |
| } |
| |
| uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); } |
| |
| Type* GetVoidType() { |
| Void void_type; |
| return GetRegisteredType(&void_type); |
| } |
| |
| uint32_t GetVoidTypeId() { return GetTypeInstruction(GetVoidType()); } |
| |
| private: |
| using TypeToIdMap = std::unordered_map<const Type*, uint32_t, HashTypePointer, |
| CompareTypePointers>; |
| using TypePool = |
| std::unordered_set<std::unique_ptr<Type>, HashTypeUniquePointer, |
| CompareTypeUniquePointers>; |
| |
| class UnresolvedType { |
| public: |
| UnresolvedType(uint32_t i, Type* t) : id_(i), type_(t) {} |
| UnresolvedType(const UnresolvedType&) = delete; |
| UnresolvedType(UnresolvedType&& that) |
| : id_(that.id_), type_(std::move(that.type_)) {} |
| |
| uint32_t id() { return id_; } |
| Type* type() { return type_.get(); } |
| std::unique_ptr<Type>&& ReleaseType() { return std::move(type_); } |
| void ResetType(Type* t) { type_.reset(t); } |
| |
| private: |
| uint32_t id_; |
| std::unique_ptr<Type> type_; |
| }; |
| using IdToUnresolvedType = std::vector<UnresolvedType>; |
| |
| // Analyzes the types and decorations on types in the given |module|. |
| void AnalyzeTypes(const Module& module); |
| |
| IRContext* context() { return context_; } |
| |
| // Attaches the decorations on |type| to |id|. |
| void AttachDecorations(uint32_t id, const Type* type); |
| |
| // Create the annotation instruction. |
| // |
| // If |is_member| is false, an OpDecorate of |decoration| on |id| is created, |
| // otherwise an OpMemberDecorate is created at |element|. The annotation is |
| // registered with the DefUseManager and the DecorationManager. |
| void CreateDecoration(uint32_t id, const std::vector<uint32_t>& decoration, |
| bool is_member = false, uint32_t element = 0); |
| |
| // Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if |
| // the given instruction is not for defining a type. |
| Type* RecordIfTypeDefinition(const Instruction& inst); |
| |
| // Returns an equivalent pointer to |type| built in terms of pointers owned by |
| // |type_pool_|. For example, if |type| is a vec3 of bool, it will be rebuilt |
| // replacing the bool subtype with one owned by |type_pool_|. |
| // |
| // The re-built type will have ID |type_id|. |
| Type* RebuildType(uint32_t type_id, const Type& type); |
| |
| // Completes the incomplete type |type|, by replaces all references to |
| // ForwardPointer by the defining Pointer. |
| void ReplaceForwardPointers(Type* type); |
| |
| // Replaces all references to |original_type| in |incomplete_types_| by |
| // |new_type|. |
| void ReplaceType(Type* new_type, Type* original_type); |
| |
| const MessageConsumer& consumer_; // Message consumer. |
| IRContext* context_; |
| IdToTypeMap id_to_type_; // Mapping from ids to their type representations. |
| TypeToIdMap type_to_id_; // Mapping from types to their defining ids. |
| TypePool type_pool_; // Memory owner of type pointers. |
| IdToUnresolvedType incomplete_types_; // All incomplete types. Stored in an |
| // std::vector to make traversals |
| // deterministic. |
| |
| IdToTypeMap id_to_incomplete_type_; // Maps ids to their type representations |
| // for incomplete types. |
| |
| std::unordered_map<uint32_t, const Instruction*> id_to_constant_inst_; |
| }; |
| |
| } // namespace analysis |
| } // namespace opt |
| } // namespace spvtools |
| |
| #endif // SOURCE_OPT_TYPE_MANAGER_H_ |