• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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