Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2015-2016 The Khronos Group 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_VAL_FUNCTION_H_
     16 #define SOURCE_VAL_FUNCTION_H_
     17 
     18 #include <functional>
     19 #include <list>
     20 #include <map>
     21 #include <set>
     22 #include <string>
     23 #include <unordered_map>
     24 #include <unordered_set>
     25 #include <utility>
     26 #include <vector>
     27 
     28 #include "source/latest_version_spirv_header.h"
     29 #include "source/val/basic_block.h"
     30 #include "source/val/construct.h"
     31 #include "spirv-tools/libspirv.h"
     32 
     33 namespace spvtools {
     34 namespace val {
     35 
     36 struct bb_constr_type_pair_hash {
     37   std::size_t operator()(
     38       const std::pair<const BasicBlock*, ConstructType>& p) const {
     39     auto h1 = std::hash<const BasicBlock*>{}(p.first);
     40     auto h2 = std::hash<std::underlying_type<ConstructType>::type>{}(
     41         static_cast<std::underlying_type<ConstructType>::type>(p.second));
     42     return (h1 ^ h2);
     43   }
     44 };
     45 
     46 enum class FunctionDecl {
     47   kFunctionDeclUnknown,      /// < Unknown function declaration
     48   kFunctionDeclDeclaration,  /// < Function declaration
     49   kFunctionDeclDefinition    /// < Function definition
     50 };
     51 
     52 /// This class manages all function declaration and definitions in a module. It
     53 /// handles the state and id information while parsing a function in the SPIR-V
     54 /// binary.
     55 class Function {
     56  public:
     57   Function(uint32_t id, uint32_t result_type_id,
     58            SpvFunctionControlMask function_control, uint32_t function_type_id);
     59 
     60   /// Registers a function parameter in the current function
     61   /// @return Returns SPV_SUCCESS if the call was successful
     62   spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
     63 
     64   /// Sets the declaration type of the current function
     65   /// @return Returns SPV_SUCCESS if the call was successful
     66   spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
     67 
     68   /// Registers a block in the current function. Subsequent block instructions
     69   /// will target this block
     70   /// @param id The ID of the label of the block
     71   /// @return Returns SPV_SUCCESS if the call was successful
     72   spv_result_t RegisterBlock(uint32_t id, bool is_definition = true);
     73 
     74   /// Registers a variable in the current block
     75   ///
     76   /// @param[in] type_id The type ID of the varaible
     77   /// @param[in] id      The ID of the varaible
     78   /// @param[in] storage The storage of the variable
     79   /// @param[in] init_id The initializer ID of the variable
     80   ///
     81   /// @return Returns SPV_SUCCESS if the call was successful
     82   spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
     83                                      SpvStorageClass storage, uint32_t init_id);
     84 
     85   /// Registers a loop merge construct in the function
     86   ///
     87   /// @param[in] merge_id The merge block ID of the loop
     88   /// @param[in] continue_id The continue block ID of the loop
     89   ///
     90   /// @return Returns SPV_SUCCESS if the call was successful
     91   spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id);
     92 
     93   /// Registers a selection merge construct in the function
     94   /// @return Returns SPV_SUCCESS if the call was successful
     95   spv_result_t RegisterSelectionMerge(uint32_t merge_id);
     96 
     97   /// Registers the end of the block
     98   ///
     99   /// @param[in] successors_list A list of ids to the block's successors
    100   /// @param[in] branch_instruction the branch instruction that ended the block
    101   void RegisterBlockEnd(std::vector<uint32_t> successors_list,
    102                         SpvOp branch_instruction);
    103 
    104   /// Registers the end of the function.  This is idempotent.
    105   void RegisterFunctionEnd();
    106 
    107   /// Returns true if the \p id block is the first block of this function
    108   bool IsFirstBlock(uint32_t id) const;
    109 
    110   /// Returns true if the \p merge_block_id is a BlockType of \p type
    111   bool IsBlockType(uint32_t merge_block_id, BlockType type) const;
    112 
    113   /// Returns a pair consisting of the BasicBlock with \p id and a bool
    114   /// which is true if the block has been defined, and false if it is
    115   /// declared but not defined. This function will return nullptr if the
    116   /// \p id was not declared and not defined at the current point in the binary
    117   std::pair<const BasicBlock*, bool> GetBlock(uint32_t id) const;
    118   std::pair<BasicBlock*, bool> GetBlock(uint32_t id);
    119 
    120   /// Returns the first block of the current function
    121   const BasicBlock* first_block() const;
    122 
    123   /// Returns the first block of the current function
    124   BasicBlock* first_block();
    125 
    126   /// Returns a vector of all the blocks in the function
    127   const std::vector<BasicBlock*>& ordered_blocks() const;
    128 
    129   /// Returns a vector of all the blocks in the function
    130   std::vector<BasicBlock*>& ordered_blocks();
    131 
    132   /// Returns a list of all the cfg constructs in the function
    133   const std::list<Construct>& constructs() const;
    134 
    135   /// Returns a list of all the cfg constructs in the function
    136   std::list<Construct>& constructs();
    137 
    138   /// Returns the number of blocks in the current function being parsed
    139   size_t block_count() const;
    140 
    141   /// Returns the id of the function
    142   uint32_t id() const { return id_; }
    143 
    144   /// Returns return type id of the function
    145   uint32_t GetResultTypeId() const { return result_type_id_; }
    146 
    147   /// Returns the number of blocks in the current function being parsed
    148   size_t undefined_block_count() const;
    149   const std::unordered_set<uint32_t>& undefined_blocks() const {
    150     return undefined_blocks_;
    151   }
    152 
    153   /// Returns the block that is currently being parsed in the binary
    154   BasicBlock* current_block();
    155 
    156   /// Returns the block that is currently being parsed in the binary
    157   const BasicBlock* current_block() const;
    158 
    159   // For dominance calculations, we want to analyze all the
    160   // blocks in the function, even in degenerate control flow cases
    161   // including unreachable blocks.  We therefore make an "augmented CFG"
    162   // which is the same as the ordinary CFG but adds:
    163   //  - A pseudo-entry node.
    164   //  - A pseudo-exit node.
    165   //  - A minimal set of edges so that a forward traversal from the
    166   //    pseudo-entry node will visit all nodes.
    167   //  - A minimal set of edges so that a backward traversal from the
    168   //    pseudo-exit node will visit all nodes.
    169   // In particular, the pseudo-entry node is the unique source of the
    170   // augmented CFG, and the psueo-exit node is the unique sink of the
    171   // augmented CFG.
    172 
    173   /// Returns the pseudo exit block
    174   BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; }
    175 
    176   /// Returns the pseudo exit block
    177   const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; }
    178 
    179   /// Returns the pseudo exit block
    180   BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; }
    181 
    182   /// Returns the pseudo exit block
    183   const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; }
    184 
    185   using GetBlocksFunction =
    186       std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
    187   /// Returns the block successors function for the augmented CFG.
    188   GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
    189   /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from
    190   /// a loop header block to its continue target, if they are different blocks.
    191   GetBlocksFunction
    192   AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
    193   /// Returns the block predecessors function for the augmented CFG.
    194   GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
    195 
    196   /// Returns the control flow nesting depth of the given basic block.
    197   /// This function only works when you have structured control flow.
    198   /// This function should only be called after the control flow constructs have
    199   /// been identified and dominators have been computed.
    200   int GetBlockDepth(BasicBlock* bb);
    201 
    202   /// Prints a GraphViz digraph of the CFG of the current funciton
    203   void PrintDotGraph() const;
    204 
    205   /// Prints a directed graph of the CFG of the current funciton
    206   void PrintBlocks() const;
    207 
    208   /// Registers execution model limitation such as "Feature X is only available
    209   /// with Execution Model Y".
    210   void RegisterExecutionModelLimitation(SpvExecutionModel model,
    211                                         const std::string& message);
    212 
    213   /// Registers execution model limitation with an |is_compatible| functor.
    214   void RegisterExecutionModelLimitation(
    215       std::function<bool(SpvExecutionModel, std::string*)> is_compatible) {
    216     execution_model_limitations_.push_back(is_compatible);
    217   }
    218 
    219   /// Returns true if the given execution model passes the limitations stored in
    220   /// execution_model_limitations_. Returns false otherwise and fills optional
    221   /// |reason| parameter.
    222   bool IsCompatibleWithExecutionModel(SpvExecutionModel model,
    223                                       std::string* reason = nullptr) const;
    224 
    225   // Inserts id to the set of functions called from this function.
    226   void AddFunctionCallTarget(uint32_t call_target_id) {
    227     function_call_targets_.insert(call_target_id);
    228   }
    229 
    230   // Returns a set with ids of all functions called from this function.
    231   const std::set<uint32_t> function_call_targets() const {
    232     return function_call_targets_;
    233   }
    234 
    235  private:
    236   // Computes the representation of the augmented CFG.
    237   // Populates augmented_successors_map_ and augmented_predecessors_map_.
    238   void ComputeAugmentedCFG();
    239 
    240   // Adds a copy of the given Construct, and tracks it by its entry block.
    241   // Returns a reference to the stored construct.
    242   Construct& AddConstruct(const Construct& new_construct);
    243 
    244   // Returns a reference to the construct corresponding to the given entry
    245   // block.
    246   Construct& FindConstructForEntryBlock(const BasicBlock* entry_block,
    247                                         ConstructType t);
    248 
    249   /// The result id of the OpLabel that defined this block
    250   uint32_t id_;
    251 
    252   /// The type of the function
    253   uint32_t function_type_id_;
    254 
    255   /// The type of the return value
    256   uint32_t result_type_id_;
    257 
    258   /// The control fo the funciton
    259   SpvFunctionControlMask function_control_;
    260 
    261   /// The type of declaration of each function
    262   FunctionDecl declaration_type_;
    263 
    264   // Have we finished parsing this function?
    265   bool end_has_been_registered_;
    266 
    267   /// The blocks in the function mapped by block ID
    268   std::unordered_map<uint32_t, BasicBlock> blocks_;
    269 
    270   /// A list of blocks in the order they appeared in the binary
    271   std::vector<BasicBlock*> ordered_blocks_;
    272 
    273   /// Blocks which are forward referenced by blocks but not defined
    274   std::unordered_set<uint32_t> undefined_blocks_;
    275 
    276   /// The block that is currently being parsed
    277   BasicBlock* current_block_;
    278 
    279   /// A pseudo entry node used in dominance analysis.
    280   /// After the function end has been registered, the successor list of the
    281   /// pseudo entry node is the minimal set of nodes such that all nodes in the
    282   /// CFG can be reached by following successor lists.  That is, the successors
    283   /// will be:
    284   ///   - Any basic block without predecessors.  This includes the entry
    285   ///     block to the function.
    286   ///   - A single node from each otherwise unreachable cycle in the CFG, if
    287   ///     such cycles exist.
    288   /// The pseudo entry node does not appear in the predecessor or successor
    289   /// list of any ordinary block.
    290   /// It has no predecessors.
    291   /// It has Id 0.
    292   BasicBlock pseudo_entry_block_;
    293 
    294   /// A pseudo exit block used in dominance analysis.
    295   /// After the function end has been registered, the predecessor list of the
    296   /// pseudo exit node is the minimal set of nodes such that all nodes in the
    297   /// CFG can be reached by following predecessor lists.  That is, the
    298   /// predecessors will be:
    299   ///   - Any basic block without successors.  This includes any basic block
    300   ///     ending with an OpReturn, OpReturnValue or similar instructions.
    301   ///   - A single node from each otherwise unreachable cycle in the CFG, if
    302   ///     such cycles exist.
    303   /// The pseudo exit node does not appear in the predecessor or successor
    304   /// list of any ordinary block.
    305   /// It has no successors.
    306   BasicBlock pseudo_exit_block_;
    307 
    308   // Maps a block to its successors in the augmented CFG, if that set is
    309   // different from its successors in the ordinary CFG.
    310   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
    311       augmented_successors_map_;
    312   // Maps a block to its predecessors in the augmented CFG, if that set is
    313   // different from its predecessors in the ordinary CFG.
    314   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
    315       augmented_predecessors_map_;
    316 
    317   // Maps a structured loop header to its CFG successors and also its
    318   // continue target if that continue target is not the loop header
    319   // itself. This might have duplicates.
    320   std::unordered_map<const BasicBlock*, std::vector<BasicBlock*>>
    321       loop_header_successors_plus_continue_target_map_;
    322 
    323   /// The constructs that are available in this function
    324   std::list<Construct> cfg_constructs_;
    325 
    326   /// The variable IDs of the functions
    327   std::vector<uint32_t> variable_ids_;
    328 
    329   /// The function parameter ids of the functions
    330   std::vector<uint32_t> parameter_ids_;
    331 
    332   /// Maps a construct's entry block to the construct(s).
    333   /// Since a basic block may be the entry block of different types of
    334   /// constructs, the type of the construct should also be specified in order to
    335   /// get the unique construct.
    336   std::unordered_map<std::pair<const BasicBlock*, ConstructType>, Construct*,
    337                      bb_constr_type_pair_hash>
    338       entry_block_to_construct_;
    339 
    340   /// This map provides the header block for a given merge block.
    341   std::unordered_map<BasicBlock*, BasicBlock*> merge_block_header_;
    342 
    343   /// Stores the control flow nesting depth of a given basic block
    344   std::unordered_map<BasicBlock*, int> block_depth_;
    345 
    346   /// Stores execution model limitations imposed by instructions used within the
    347   /// function. The functor stored in the list return true if execution model
    348   /// is compatible, false otherwise. If the functor returns false, it can also
    349   /// optionally fill the string parameter with the reason for incompatibility.
    350   std::list<std::function<bool(SpvExecutionModel, std::string*)>>
    351       execution_model_limitations_;
    352 
    353   /// Stores ids of all functions called from this function.
    354   std::set<uint32_t> function_call_targets_;
    355 };
    356 
    357 }  // namespace val
    358 }  // namespace spvtools
    359 
    360 #endif  // SOURCE_VAL_FUNCTION_H_
    361