• 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_PASS_H_
16 #define SOURCE_FUZZ_FUZZER_PASS_H_
17 
18 #include <functional>
19 #include <vector>
20 
21 #include "source/fuzz/fuzzer_context.h"
22 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
23 #include "source/fuzz/transformation_context.h"
24 #include "source/opt/ir_context.h"
25 
26 namespace spvtools {
27 namespace fuzz {
28 
29 // Interface for applying a pass of transformations to a module.
30 class FuzzerPass {
31  public:
32   FuzzerPass(opt::IRContext* ir_context,
33              TransformationContext* transformation_context,
34              FuzzerContext* fuzzer_context,
35              protobufs::TransformationSequence* transformations);
36 
37   virtual ~FuzzerPass();
38 
39   // Applies the pass to the module |ir_context_|, assuming and updating
40   // information from |transformation_context_|, and using |fuzzer_context_| to
41   // guide the process.  Appends to |transformations_| all transformations that
42   // were applied during the pass.
43   virtual void Apply() = 0;
44 
45  protected:
GetIRContext()46   opt::IRContext* GetIRContext() const { return ir_context_; }
47 
GetTransformationContext()48   TransformationContext* GetTransformationContext() const {
49     return transformation_context_;
50   }
51 
GetFuzzerContext()52   FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; }
53 
GetTransformations()54   protobufs::TransformationSequence* GetTransformations() const {
55     return transformations_;
56   }
57 
58   // Returns all instructions that are *available* at |inst_it|, which is
59   // required to be inside block |block| of function |function| - that is, all
60   // instructions at global scope and all instructions that strictly dominate
61   // |inst_it|.
62   //
63   // Filters said instructions to return only those that satisfy the
64   // |instruction_is_relevant| predicate.  This, for instance, could ignore all
65   // instructions that have a particular decoration.
66   std::vector<opt::Instruction*> FindAvailableInstructions(
67       opt::Function* function, opt::BasicBlock* block,
68       const opt::BasicBlock::iterator& inst_it,
69       std::function<bool(opt::IRContext*, opt::Instruction*)>
70           instruction_is_relevant) const;
71 
72   // A helper method that iterates through each instruction in each block, at
73   // all times tracking an instruction descriptor that allows the latest
74   // instruction to be located even if it has no result id.
75   //
76   // The code to manipulate the instruction descriptor is a bit fiddly.  The
77   // point of this method is to avoiding having to duplicate it in multiple
78   // transformation passes.
79   //
80   // The function |action| is invoked for each instruction |inst_it| in block
81   // |block| of function |function| that is encountered.  The
82   // |instruction_descriptor| parameter to the function object allows |inst_it|
83   // to be identified.
84   //
85   // In most intended use cases, the job of |action| is to randomly decide
86   // whether to try to apply some transformation, and then - if selected - to
87   // attempt to apply it.
88   void ForEachInstructionWithInstructionDescriptor(
89       std::function<
90           void(opt::Function* function, opt::BasicBlock* block,
91                opt::BasicBlock::iterator inst_it,
92                const protobufs::InstructionDescriptor& instruction_descriptor)>
93           action);
94 
95   // A generic helper for applying a transformation that should be applicable
96   // by construction, and adding it to the sequence of applied transformations.
97   template <typename TransformationType>
ApplyTransformation(const TransformationType & transformation)98   void ApplyTransformation(const TransformationType& transformation) {
99     assert(transformation.IsApplicable(GetIRContext(),
100                                        *GetTransformationContext()) &&
101            "Transformation should be applicable by construction.");
102     transformation.Apply(GetIRContext(), GetTransformationContext());
103     *GetTransformations()->add_transformation() = transformation.ToMessage();
104   }
105 
106   // Returns the id of an OpTypeBool instruction.  If such an instruction does
107   // not exist, a transformation is applied to add it.
108   uint32_t FindOrCreateBoolType();
109 
110   // Returns the id of an OpTypeInt instruction, with width 32 and signedness
111   // specified by |is_signed|.  If such an instruction does not exist, a
112   // transformation is applied to add it.
113   uint32_t FindOrCreate32BitIntegerType(bool is_signed);
114 
115   // Returns the id of an OpTypeFloat instruction, with width 32.  If such an
116   // instruction does not exist, a transformation is applied to add it.
117   uint32_t FindOrCreate32BitFloatType();
118 
119   // Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
120   // instruction. If such an instruction doesn't exist, a transformation
121   // is applied to create a new one.
122   uint32_t FindOrCreateFunctionType(uint32_t return_type_id,
123                                     const std::vector<uint32_t>& argument_id);
124 
125   // Returns the id of an OpTypeVector instruction, with |component_type_id|
126   // (which must already exist) as its base type, and |component_count|
127   // elements (which must be in the range [2, 4]).  If such an instruction does
128   // not exist, a transformation is applied to add it.
129   uint32_t FindOrCreateVectorType(uint32_t component_type_id,
130                                   uint32_t component_count);
131 
132   // Returns the id of an OpTypeMatrix instruction, with |column_count| columns
133   // and |row_count| rows (each of which must be in the range [2, 4]).  If the
134   // float and vector types required to build this matrix type or the matrix
135   // type itself do not exist, transformations are applied to add them.
136   uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count);
137 
138   // Returns the id of a pointer type with base type |base_type_id| (which must
139   // already exist) and storage class |storage_class|.  A transformation is
140   // applied to add the pointer if it does not already exist.
141   uint32_t FindOrCreatePointerType(uint32_t base_type_id,
142                                    SpvStorageClass storage_class);
143 
144   // Returns the id of an OpTypePointer instruction, with a 32-bit integer base
145   // type of signedness specified by |is_signed|.  If the pointer type or
146   // required integer base type do not exist, transformations are applied to add
147   // them.
148   uint32_t FindOrCreatePointerTo32BitIntegerType(bool is_signed,
149                                                  SpvStorageClass storage_class);
150 
151   // Returns the id of an OpConstant instruction, with 32-bit integer type of
152   // signedness specified by |is_signed|, with |word| as its value.  If either
153   // the required integer type or the constant do not exist, transformations are
154   // applied to add them.
155   uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);
156 
157   // Returns the id of an OpConstant instruction, with 32-bit floating-point
158   // type, with |word| as its value.  If either the required floating-point type
159   // or the constant do not exist, transformations are applied to add them.
160   uint32_t FindOrCreate32BitFloatConstant(uint32_t word);
161 
162   // Returns the id of an OpConstantTrue or OpConstantFalse instruction,
163   // according to |value|.  If either the required instruction or the bool
164   // type do not exist, transformations are applied to add them.
165   uint32_t FindOrCreateBoolConstant(bool value);
166 
167   // Returns the result id of an instruction of the form:
168   //   %id = OpUndef %|type_id|
169   // If no such instruction exists, a transformation is applied to add it.
170   uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
171 
172   // Define a *basic type* to be an integer, boolean or floating-point type,
173   // or a matrix, vector, struct or fixed-size array built from basic types.  In
174   // particular, a basic type cannot contain an opaque type (such as an image),
175   // or a runtime-sized array.
176   //
177   // Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that:
178   // - basic_type_ids captures every basic type declared in the module.
179   // - basic_type_ids_to_pointers maps every such basic type to the sequence
180   //   of all pointer types that have storage class |storage_class| and the
181   //   given basic type as their pointee type.  The sequence may be empty for
182   //   some basic types if no pointers to those types are defined for the given
183   //   storage class, and the sequence will have multiple elements if there are
184   //   repeated pointer declarations for the same basic type and storage class.
185   std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
186   GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const;
187 
188   // Given a type id, |scalar_or_composite_type_id|, which must correspond to
189   // some scalar or composite type, returns the result id of an instruction
190   // defining a constant of the given type that is zero or false at everywhere.
191   // If such an instruction does not yet exist, transformations are applied to
192   // add it.
193   //
194   // Examples:
195   // --------------+-------------------------------
196   //   TYPE        | RESULT is id corresponding to
197   // --------------+-------------------------------
198   //   bool        | false
199   // --------------+-------------------------------
200   //   bvec4       | (false, false, false, false)
201   // --------------+-------------------------------
202   //   float       | 0.0
203   // --------------+-------------------------------
204   //   vec2        | (0.0, 0.0)
205   // --------------+-------------------------------
206   //   int[3]      | [0, 0, 0]
207   // --------------+-------------------------------
208   //   struct S {  |
209   //     int i;    | S(0, false, (0u, 0u))
210   //     bool b;   |
211   //     uint2 u;  |
212   //   }           |
213   // --------------+-------------------------------
214   uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
215 
216  private:
217   // Array, matrix and vector are *homogeneous* composite types in the sense
218   // that every component of one of these types has the same type.  Given a
219   // homogeneous composite type instruction, |composite_type_instruction|,
220   // returns the id of a composite constant instruction for which every element
221   // is zero/false.  If such an instruction does not yet exist, transformations
222   // are applied to add it.
223   uint32_t GetZeroConstantForHomogeneousComposite(
224       const opt::Instruction& composite_type_instruction,
225       uint32_t component_type_id, uint32_t num_components);
226 
227   // Helper to find an existing composite constant instruction of the given
228   // composite type with the given constant components, or to apply
229   // transformations to create such an instruction if it does not yet exist.
230   // Parameter |composite_type_instruction| must be a composite type
231   // instruction.  The parameters |constants| and |constant_ids| must have the
232   // same size, and it must be the case that for each i, |constant_ids[i]| is
233   // the result id of an instruction that defines |constants[i]|.
234   uint32_t FindOrCreateCompositeConstant(
235       const opt::Instruction& composite_type_instruction,
236       const std::vector<const opt::analysis::Constant*>& constants,
237       const std::vector<uint32_t>& constant_ids);
238 
239   opt::IRContext* ir_context_;
240   TransformationContext* transformation_context_;
241   FuzzerContext* fuzzer_context_;
242   protobufs::TransformationSequence* transformations_;
243 };
244 
245 }  // namespace fuzz
246 }  // namespace spvtools
247 
248 #endif  // SOURCE_FUZZ_FUZZER_PASS_H_
249