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