• 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 // instantiated with |validator_options|.
137 bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
138 
139 // Returns a clone of |context|, by writing |context| to a binary and then
140 // parsing it again.
141 std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context);
142 
143 // Returns true if and only if |id| is the id of a type that is not a function
144 // type.
145 bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
146 
147 // Returns true if and only if |block_id| is a merge block or continue target
148 bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
149 
150 // Returns the result id of an instruction of the form:
151 //  %id = OpTypeFunction |type_ids|
152 // or 0 if no such instruction exists.
153 uint32_t FindFunctionType(opt::IRContext* ir_context,
154                           const std::vector<uint32_t>& type_ids);
155 
156 // Returns a type instruction (OpTypeFunction) for |function|.
157 // Returns |nullptr| if type is not found.
158 opt::Instruction* GetFunctionType(opt::IRContext* context,
159                                   const opt::Function* function);
160 
161 // Returns the function with result id |function_id|, or |nullptr| if no such
162 // function exists.
163 opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
164 
165 // Returns |true| if one of entry points has function id |function_id|.
166 bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
167 
168 // Checks whether |id| is available (according to dominance rules) at the use
169 // point defined by input operand |use_input_operand_index| of
170 // |use_instruction|.
171 bool IdIsAvailableAtUse(opt::IRContext* context,
172                         opt::Instruction* use_instruction,
173                         uint32_t use_input_operand_index, uint32_t id);
174 
175 // Checks whether |id| is available (according to dominance rules) at the
176 // program point directly before |instruction|.
177 bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
178                                     opt::Instruction* instruction, uint32_t id);
179 
180 // Returns true if and only if |instruction| is an OpFunctionParameter
181 // associated with |function|.
182 bool InstructionIsFunctionParameter(opt::Instruction* instruction,
183                                     opt::Function* function);
184 
185 // Returns the type id of the instruction defined by |result_id|, or 0 if there
186 // is no such result id.
187 uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id);
188 
189 // Given |pointer_type_inst|, which must be an OpTypePointer instruction,
190 // returns the id of the associated pointee type.
191 uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst);
192 
193 // Given |pointer_type_id|, which must be the id of a pointer type, returns the
194 // id of the associated pointee type.
195 uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
196                                          uint32_t pointer_type_id);
197 
198 // Given |pointer_type_inst|, which must be an OpTypePointer instruction,
199 // returns the associated storage class.
200 SpvStorageClass GetStorageClassFromPointerType(
201     opt::Instruction* pointer_type_inst);
202 
203 // Given |pointer_type_id|, which must be the id of a pointer type, returns the
204 // associated storage class.
205 SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
206                                                uint32_t pointer_type_id);
207 
208 // Returns the id of a pointer with pointee type |pointee_type_id| and storage
209 // class |storage_class|, if it exists, and 0 otherwise.
210 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
211                              SpvStorageClass storage_class);
212 
213 // Returns true if and only if |type| is one of the types for which it is legal
214 // to have an OpConstantNull value.
215 bool IsNullConstantSupported(const opt::analysis::Type& type);
216 
217 }  // namespace fuzzerutil
218 
219 }  // namespace fuzz
220 }  // namespace spvtools
221 
222 #endif  // SOURCE_FUZZ_FUZZER_UTIL_H_
223