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 { operatorbb_constr_type_pair_hash37 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 id()142 uint32_t id() const { return id_; } 143 144 /// Returns return type id of the function GetResultTypeId()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; undefined_blocks()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 pseudo_entry_block()174 BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } 175 176 /// Returns the pseudo exit block pseudo_entry_block()177 const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; } 178 179 /// Returns the pseudo exit block pseudo_exit_block()180 BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } 181 182 /// Returns the pseudo exit block 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. RegisterExecutionModelLimitation(std::function<bool (SpvExecutionModel,std::string *)> is_compatible)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. AddFunctionCallTarget(uint32_t call_target_id)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. function_call_targets()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