Home | History | Annotate | Download | only in opt
      1 // Copyright (c) 2016 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #ifndef SOURCE_OPT_TYPE_MANAGER_H_
     16 #define SOURCE_OPT_TYPE_MANAGER_H_
     17 
     18 #include <memory>
     19 #include <unordered_map>
     20 #include <unordered_set>
     21 #include <utility>
     22 #include <vector>
     23 
     24 #include "source/opt/module.h"
     25 #include "source/opt/types.h"
     26 #include "spirv-tools/libspirv.hpp"
     27 
     28 namespace spvtools {
     29 namespace opt {
     30 
     31 class IRContext;
     32 
     33 namespace analysis {
     34 
     35 // Hashing functor.
     36 //
     37 // All type pointers must be non-null.
     38 struct HashTypePointer {
     39   size_t operator()(const Type* type) const {
     40     assert(type);
     41     return type->HashValue();
     42   }
     43 };
     44 struct HashTypeUniquePointer {
     45   size_t operator()(const std::unique_ptr<Type>& type) const {
     46     assert(type);
     47     return type->HashValue();
     48   }
     49 };
     50 
     51 // Equality functor.
     52 //
     53 // Checks if two types pointers are the same type.
     54 //
     55 // All type pointers must be non-null.
     56 struct CompareTypePointers {
     57   bool operator()(const Type* lhs, const Type* rhs) const {
     58     assert(lhs && rhs);
     59     return lhs->IsSame(rhs);
     60   }
     61 };
     62 struct CompareTypeUniquePointers {
     63   bool operator()(const std::unique_ptr<Type>& lhs,
     64                   const std::unique_ptr<Type>& rhs) const {
     65     assert(lhs && rhs);
     66     return lhs->IsSame(rhs.get());
     67   }
     68 };
     69 
     70 // A class for managing the SPIR-V type hierarchy.
     71 class TypeManager {
     72  public:
     73   using IdToTypeMap = std::unordered_map<uint32_t, Type*>;
     74 
     75   // Constructs a type manager from the given |module|. All internal messages
     76   // will be communicated to the outside via the given message |consumer|.
     77   // This instance only keeps a reference to the |consumer|, so the |consumer|
     78   // should outlive this instance.
     79   TypeManager(const MessageConsumer& consumer, IRContext* c);
     80 
     81   TypeManager(const TypeManager&) = delete;
     82   TypeManager(TypeManager&&) = delete;
     83   TypeManager& operator=(const TypeManager&) = delete;
     84   TypeManager& operator=(TypeManager&&) = delete;
     85 
     86   // Returns the type for the given type |id|. Returns nullptr if the given |id|
     87   // does not define a type.
     88   Type* GetType(uint32_t id) const;
     89   // Returns the id for the given |type|. Returns 0 if can not find the given
     90   // |type|.
     91   uint32_t GetId(const Type* type) const;
     92   // Returns the number of types hold in this manager.
     93   size_t NumTypes() const { return id_to_type_.size(); }
     94   // Iterators for all types contained in this manager.
     95   IdToTypeMap::const_iterator begin() const { return id_to_type_.cbegin(); }
     96   IdToTypeMap::const_iterator end() const { return id_to_type_.cend(); }
     97 
     98   // Returns a pair of the type and pointer to the type in |sc|.
     99   //
    100   // |id| must be a registered type.
    101   std::pair<Type*, std::unique_ptr<Pointer>> GetTypeAndPointerType(
    102       uint32_t id, SpvStorageClass sc) const;
    103 
    104   // Returns an id for a declaration representing |type|.
    105   //
    106   // If |type| is registered, then the registered id is returned. Otherwise,
    107   // this function recursively adds type and annotation instructions as
    108   // necessary to fully define |type|.
    109   uint32_t GetTypeInstruction(const Type* type);
    110 
    111   // Find pointer to type and storage in module, return its resultId.  If it is
    112   // not found, a new type is created, and its id is returned.
    113   uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class);
    114 
    115   // Registers |id| to |type|.
    116   //
    117   // If GetId(|type|) already returns a non-zero id, that mapping will be
    118   // unchanged.
    119   void RegisterType(uint32_t id, const Type& type);
    120 
    121   Type* GetRegisteredType(const Type* type);
    122 
    123   // Removes knowledge of |id| from the manager.
    124   //
    125   // If |id| is an ambiguous type the multiple ids may be registered to |id|'s
    126   // type (e.g. %struct1 and %struct1 might hash to the same type). In that
    127   // case, calling GetId() with |id|'s type will return another suitable id
    128   // defining that type.
    129   void RemoveId(uint32_t id);
    130 
    131   // Returns the type of the member of |parent_type| that is identified by
    132   // |access_chain|.  The vector |access_chain| is a series of integers that are
    133   // used to pick members as in the |OpCompositeExtract| instructions.  If you
    134   // want a member of an array, vector, or matrix that does not have a constant
    135   // index, you can use 0 in that position.  All elements have the same type.
    136   const Type* GetMemberType(const Type* parent_type,
    137                             const std::vector<uint32_t>& access_chain);
    138 
    139  private:
    140   using TypeToIdMap = std::unordered_map<const Type*, uint32_t, HashTypePointer,
    141                                          CompareTypePointers>;
    142   using TypePool =
    143       std::unordered_set<std::unique_ptr<Type>, HashTypeUniquePointer,
    144                          CompareTypeUniquePointers>;
    145 
    146   class UnresolvedType {
    147    public:
    148     UnresolvedType(uint32_t i, Type* t) : id_(i), type_(t) {}
    149     UnresolvedType(const UnresolvedType&) = delete;
    150     UnresolvedType(UnresolvedType&& that)
    151         : id_(that.id_), type_(std::move(that.type_)) {}
    152 
    153     uint32_t id() { return id_; }
    154     Type* type() { return type_.get(); }
    155     std::unique_ptr<Type>&& ReleaseType() { return std::move(type_); }
    156     void ResetType(Type* t) { type_.reset(t); }
    157 
    158    private:
    159     uint32_t id_;
    160     std::unique_ptr<Type> type_;
    161   };
    162   using IdToUnresolvedType = std::vector<UnresolvedType>;
    163 
    164   // Analyzes the types and decorations on types in the given |module|.
    165   void AnalyzeTypes(const Module& module);
    166 
    167   IRContext* context() { return context_; }
    168 
    169   // Attaches the decorations on |type| to |id|.
    170   void AttachDecorations(uint32_t id, const Type* type);
    171 
    172   // Create the annotation instruction.
    173   //
    174   // If |element| is zero, an OpDecorate is created, other an OpMemberDecorate
    175   // is created. The annotation is registered with the DefUseManager and the
    176   // DecorationManager.
    177   void CreateDecoration(uint32_t id, const std::vector<uint32_t>& decoration,
    178                         uint32_t element = 0);
    179 
    180   // Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if
    181   // the given instruction is not for defining a type.
    182   Type* RecordIfTypeDefinition(const Instruction& inst);
    183   // Attaches the decoration encoded in |inst| to |type|. Does nothing if the
    184   // given instruction is not a decoration instruction. Assumes the target is
    185   // |type| (e.g. should be called in loop of |type|'s decorations).
    186   void AttachDecoration(const Instruction& inst, Type* type);
    187 
    188   // Returns an equivalent pointer to |type| built in terms of pointers owned by
    189   // |type_pool_|. For example, if |type| is a vec3 of bool, it will be rebuilt
    190   // replacing the bool subtype with one owned by |type_pool_|.
    191   Type* RebuildType(const Type& type);
    192 
    193   // Completes the incomplete type |type|, by replaces all references to
    194   // ForwardPointer by the defining Pointer.
    195   void ReplaceForwardPointers(Type* type);
    196 
    197   // Replaces all references to |original_type| in |incomplete_types_| by
    198   // |new_type|.
    199   void ReplaceType(Type* new_type, Type* original_type);
    200 
    201   const MessageConsumer& consumer_;  // Message consumer.
    202   IRContext* context_;
    203   IdToTypeMap id_to_type_;  // Mapping from ids to their type representations.
    204   TypeToIdMap type_to_id_;  // Mapping from types to their defining ids.
    205   TypePool type_pool_;      // Memory owner of type pointers.
    206   IdToUnresolvedType incomplete_types_;  // All incomplete types.  Stored in an
    207                                          // std::vector to make traversals
    208                                          // deterministic.
    209 
    210   IdToTypeMap id_to_incomplete_type_;  // Maps ids to their type representations
    211                                        // for incomplete types.
    212 };
    213 
    214 }  // namespace analysis
    215 }  // namespace opt
    216 }  // namespace spvtools
    217 
    218 #endif  // SOURCE_OPT_TYPE_MANAGER_H_
    219