1 // Copyright (c) 2019 Google LLC 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_FUZZ_FUZZER_UTIL_H_ 16 #define SOURCE_FUZZ_FUZZER_UTIL_H_ 17 18 #include <vector> 19 20 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" 21 #include "source/opt/basic_block.h" 22 #include "source/opt/instruction.h" 23 #include "source/opt/ir_context.h" 24 25 namespace spvtools { 26 namespace fuzz { 27 28 // Provides types and global utility methods for use by the fuzzer 29 namespace fuzzerutil { 30 31 // Function type that produces a SPIR-V module. 32 using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>; 33 34 // Returns true if and only if the module does not define the given id. 35 bool IsFreshId(opt::IRContext* context, uint32_t id); 36 37 // Updates the module's id bound if needed so that it is large enough to 38 // account for the given id. 39 void UpdateModuleIdBound(opt::IRContext* context, uint32_t id); 40 41 // Return the block with id |maybe_block_id| if it exists, and nullptr 42 // otherwise. 43 opt::BasicBlock* MaybeFindBlock(opt::IRContext* context, 44 uint32_t maybe_block_id); 45 46 // When adding an edge from |bb_from| to |bb_to| (which are assumed to be blocks 47 // in the same function), it is important to supply |bb_to| with ids that can be 48 // used to augment OpPhi instructions in the case that there is not already such 49 // an edge. This function returns true if and only if the ids provided in 50 // |phi_ids| suffice for this purpose, 51 bool PhiIdsOkForNewEdge( 52 opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, 53 const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids); 54 55 // Returns the id of a boolean constant with value |value| if it exists in the 56 // module, or 0 otherwise. 57 uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value); 58 59 // Requires that a boolean constant with value |condition_value| is available, 60 // that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that 61 // bb_from ends with "OpBranch %some_block". Turns OpBranch into 62 // "OpBranchConditional |condition_value| ...", such that control will branch 63 // to %some_block, with |bb_to| being the unreachable alternative. Updates 64 // OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid. 65 void AddUnreachableEdgeAndUpdateOpPhis( 66 opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, 67 bool condition_value, 68 const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids); 69 70 // Returns true if and only if |maybe_loop_header_id| is a loop header and 71 // |block_id| is in the continue construct of the associated loop. 72 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, 73 uint32_t maybe_loop_header_id); 74 75 // If |block| contains |inst|, an iterator for |inst| is returned. 76 // Otherwise |block|->end() is returned. 77 opt::BasicBlock::iterator GetIteratorForInstruction( 78 opt::BasicBlock* block, const opt::Instruction* inst); 79 80 // Returns true if and only if there is a path to |bb| from the entry block of 81 // the function that contains |bb|. 82 bool BlockIsReachableInItsFunction(opt::IRContext* context, 83 opt::BasicBlock* bb); 84 85 // Determines whether it is OK to insert an instruction with opcode |opcode| 86 // before |instruction_in_block|. 87 bool CanInsertOpcodeBeforeInstruction( 88 SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block); 89 90 // Determines whether it is OK to make a synonym of |inst|. 91 bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst); 92 93 // Determines whether the given type is a composite; that is: an array, matrix, 94 // struct or vector. 95 bool IsCompositeType(const opt::analysis::Type* type); 96 97 // Returns a vector containing the same elements as |repeated_field|. 98 std::vector<uint32_t> RepeatedFieldToVector( 99 const google::protobuf::RepeatedField<uint32_t>& repeated_field); 100 101 // Given a type id, |base_object_type_id|, returns 0 if the type is not a 102 // composite type or if |index| is too large to be used as an index into the 103 // composite. Otherwise returns the type id of the type associated with the 104 // composite's index. 105 // 106 // Example: if |base_object_type_id| is 10, and we have: 107 // 108 // %10 = OpTypeStruct %3 %4 %5 109 // 110 // then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index 111 // is 3 or larger. 112 uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, 113 uint32_t base_object_type_id, 114 uint32_t index); 115 116 // Given a type id, |base_object_type_id|, checks that the given sequence of 117 // |indices| is suitable for indexing into this type. Returns the id of the 118 // type of the final sub-object reached via the indices if they are valid, and 119 // 0 otherwise. 120 uint32_t WalkCompositeTypeIndices( 121 opt::IRContext* context, uint32_t base_object_type_id, 122 const google::protobuf::RepeatedField<google::protobuf::uint32>& indices); 123 124 // Returns the number of members associated with |struct_type_instruction|, 125 // which must be an OpStructType instruction. 126 uint32_t GetNumberOfStructMembers( 127 const opt::Instruction& struct_type_instruction); 128 129 // Returns the constant size of the array associated with 130 // |array_type_instruction|, which must be an OpArrayType instruction. Returns 131 // 0 if there is not a static size. 132 uint32_t GetArraySize(const opt::Instruction& array_type_instruction, 133 opt::IRContext* context); 134 135 // Returns true if and only if |context| is valid, according to the validator. 136 bool IsValid(opt::IRContext* context); 137 138 // Returns a clone of |context|, by writing |context| to a binary and then 139 // parsing it again. 140 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context); 141 142 // Returns true if and only if |id| is the id of a type that is not a function 143 // type. 144 bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id); 145 146 // Returns true if and only if |block_id| is a merge block or continue target 147 bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id); 148 149 // Returns the result id of an instruction of the form: 150 // %id = OpTypeFunction |type_ids| 151 // or 0 if no such instruction exists. 152 uint32_t FindFunctionType(opt::IRContext* ir_context, 153 const std::vector<uint32_t>& type_ids); 154 155 // Returns the function with result id |function_id|, or |nullptr| if no such 156 // function exists. 157 opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); 158 159 // Checks whether |id| is available (according to dominance rules) at the use 160 // point defined by input operand |use_input_operand_index| of 161 // |use_instruction|. 162 bool IdIsAvailableAtUse(opt::IRContext* context, 163 opt::Instruction* use_instruction, 164 uint32_t use_input_operand_index, uint32_t id); 165 166 // Checks whether |id| is available (according to dominance rules) at the 167 // program point directly before |instruction|. 168 bool IdIsAvailableBeforeInstruction(opt::IRContext* context, 169 opt::Instruction* instruction, uint32_t id); 170 171 // Returns true if and only if |instruction| is an OpFunctionParameter 172 // associated with |function|. 173 bool InstructionIsFunctionParameter(opt::Instruction* instruction, 174 opt::Function* function); 175 176 // Returns the type id of the instruction defined by |result_id|, or 0 if there 177 // is no such result id. 178 uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id); 179 180 // Given |pointer_type_inst|, which must be an OpTypePointer instruction, 181 // returns the id of the associated pointee type. 182 uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst); 183 184 // Given |pointer_type_id|, which must be the id of a pointer type, returns the 185 // id of the associated pointee type. 186 uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, 187 uint32_t pointer_type_id); 188 189 // Given |pointer_type_inst|, which must be an OpTypePointer instruction, 190 // returns the associated storage class. 191 SpvStorageClass GetStorageClassFromPointerType( 192 opt::Instruction* pointer_type_inst); 193 194 // Given |pointer_type_id|, which must be the id of a pointer type, returns the 195 // associated storage class. 196 SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, 197 uint32_t pointer_type_id); 198 199 // Returns the id of a pointer with pointee type |pointee_type_id| and storage 200 // class |storage_class|, if it exists, and 0 otherwise. 201 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, 202 SpvStorageClass storage_class); 203 204 } // namespace fuzzerutil 205 206 } // namespace fuzz 207 } // namespace spvtools 208 209 #endif // SOURCE_FUZZ_FUZZER_UTIL_H_ 210