• 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 #include "source/fuzz/transformation_composite_construct.h"
16 
17 #include "source/fuzz/data_descriptor.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "source/opt/instruction.h"
21 
22 namespace spvtools {
23 namespace fuzz {
24 
TransformationCompositeConstruct(const protobufs::TransformationCompositeConstruct & message)25 TransformationCompositeConstruct::TransformationCompositeConstruct(
26     const protobufs::TransformationCompositeConstruct& message)
27     : message_(message) {}
28 
TransformationCompositeConstruct(uint32_t composite_type_id,std::vector<uint32_t> component,const protobufs::InstructionDescriptor & instruction_to_insert_before,uint32_t fresh_id)29 TransformationCompositeConstruct::TransformationCompositeConstruct(
30     uint32_t composite_type_id, std::vector<uint32_t> component,
31     const protobufs::InstructionDescriptor& instruction_to_insert_before,
32     uint32_t fresh_id) {
33   message_.set_composite_type_id(composite_type_id);
34   for (auto a_component : component) {
35     message_.add_component(a_component);
36   }
37   *message_.mutable_instruction_to_insert_before() =
38       instruction_to_insert_before;
39   message_.set_fresh_id(fresh_id);
40 }
41 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const42 bool TransformationCompositeConstruct::IsApplicable(
43     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
44   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
45     // We require the id for the composite constructor to be unused.
46     return false;
47   }
48 
49   auto insert_before =
50       FindInstruction(message_.instruction_to_insert_before(), ir_context);
51   if (!insert_before) {
52     // The instruction before which the composite should be inserted was not
53     // found.
54     return false;
55   }
56 
57   auto composite_type =
58       ir_context->get_type_mgr()->GetType(message_.composite_type_id());
59 
60   if (!fuzzerutil::IsCompositeType(composite_type)) {
61     // The type must actually be a composite.
62     return false;
63   }
64 
65   // If the type is an array, matrix, struct or vector, the components need to
66   // be suitable for constructing something of that type.
67   if (composite_type->AsArray() &&
68       !ComponentsForArrayConstructionAreOK(ir_context,
69                                            *composite_type->AsArray())) {
70     return false;
71   }
72   if (composite_type->AsMatrix() &&
73       !ComponentsForMatrixConstructionAreOK(ir_context,
74                                             *composite_type->AsMatrix())) {
75     return false;
76   }
77   if (composite_type->AsStruct() &&
78       !ComponentsForStructConstructionAreOK(ir_context,
79                                             *composite_type->AsStruct())) {
80     return false;
81   }
82   if (composite_type->AsVector() &&
83       !ComponentsForVectorConstructionAreOK(ir_context,
84                                             *composite_type->AsVector())) {
85     return false;
86   }
87 
88   // Now check whether every component being used to initialize the composite is
89   // available at the desired program point.
90   for (auto& component : message_.component()) {
91     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
92                                                     component)) {
93       return false;
94     }
95   }
96 
97   return true;
98 }
99 
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const100 void TransformationCompositeConstruct::Apply(
101     opt::IRContext* ir_context,
102     TransformationContext* transformation_context) const {
103   // Use the base and offset information from the transformation to determine
104   // where in the module a new instruction should be inserted.
105   auto insert_before_inst =
106       FindInstruction(message_.instruction_to_insert_before(), ir_context);
107   auto destination_block = ir_context->get_instr_block(insert_before_inst);
108   auto insert_before = fuzzerutil::GetIteratorForInstruction(
109       destination_block, insert_before_inst);
110 
111   // Prepare the input operands for an OpCompositeConstruct instruction.
112   opt::Instruction::OperandList in_operands;
113   for (auto& component_id : message_.component()) {
114     in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}});
115   }
116 
117   // Insert an OpCompositeConstruct instruction.
118   insert_before.InsertBefore(MakeUnique<opt::Instruction>(
119       ir_context, SpvOpCompositeConstruct, message_.composite_type_id(),
120       message_.fresh_id(), in_operands));
121 
122   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
123   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
124 
125   // Inform the fact manager that we now have new synonyms: every component of
126   // the composite is synonymous with the id used to construct that component,
127   // except in the case of a vector where a single vector id can span multiple
128   // components.
129   auto composite_type =
130       ir_context->get_type_mgr()->GetType(message_.composite_type_id());
131   uint32_t index = 0;
132   for (auto component : message_.component()) {
133     auto component_type = ir_context->get_type_mgr()->GetType(
134         ir_context->get_def_use_mgr()->GetDef(component)->type_id());
135     if (composite_type->AsVector() && component_type->AsVector()) {
136       // The case where the composite being constructed is a vector and the
137       // component provided for construction is also a vector is special.  It
138       // requires adding a synonym fact relating each element of the sub-vector
139       // to the corresponding element of the composite being constructed.
140       assert(component_type->AsVector()->element_type() ==
141              composite_type->AsVector()->element_type());
142       assert(component_type->AsVector()->element_count() <
143              composite_type->AsVector()->element_count());
144       for (uint32_t subvector_index = 0;
145            subvector_index < component_type->AsVector()->element_count();
146            subvector_index++) {
147         transformation_context->GetFactManager()->AddFactDataSynonym(
148             MakeDataDescriptor(component, {subvector_index}),
149             MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
150         index++;
151       }
152     } else {
153       // The other cases are simple: the component is made directly synonymous
154       // with the element of the composite being constructed.
155       transformation_context->GetFactManager()->AddFactDataSynonym(
156           MakeDataDescriptor(component, {}),
157           MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
158       index++;
159     }
160   }
161 }
162 
ComponentsForArrayConstructionAreOK(opt::IRContext * ir_context,const opt::analysis::Array & array_type) const163 bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK(
164     opt::IRContext* ir_context, const opt::analysis::Array& array_type) const {
165   if (array_type.length_info().words[0] !=
166       opt::analysis::Array::LengthInfo::kConstant) {
167     // We only handle constant-sized arrays.
168     return false;
169   }
170   if (array_type.length_info().words.size() != 2) {
171     // We only handle the case where the array size can be captured in a single
172     // word.
173     return false;
174   }
175   // Get the array size.
176   auto array_size = array_type.length_info().words[1];
177   if (static_cast<uint32_t>(message_.component().size()) != array_size) {
178     // The number of components must match the array size.
179     return false;
180   }
181   // Check that each component is the result id of an instruction whose type is
182   // the array's element type.
183   for (auto component_id : message_.component()) {
184     auto inst = ir_context->get_def_use_mgr()->GetDef(component_id);
185     if (inst == nullptr || !inst->type_id()) {
186       // The component does not correspond to an instruction with a result
187       // type.
188       return false;
189     }
190     auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
191     assert(component_type);
192     if (component_type != array_type.element_type()) {
193       // The component's type does not match the array's element type.
194       return false;
195     }
196   }
197   return true;
198 }
199 
ComponentsForMatrixConstructionAreOK(opt::IRContext * ir_context,const opt::analysis::Matrix & matrix_type) const200 bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK(
201     opt::IRContext* ir_context,
202     const opt::analysis::Matrix& matrix_type) const {
203   if (static_cast<uint32_t>(message_.component().size()) !=
204       matrix_type.element_count()) {
205     // The number of components must match the number of columns of the matrix.
206     return false;
207   }
208   // Check that each component is the result id of an instruction whose type is
209   // the matrix's column type.
210   for (auto component_id : message_.component()) {
211     auto inst = ir_context->get_def_use_mgr()->GetDef(component_id);
212     if (inst == nullptr || !inst->type_id()) {
213       // The component does not correspond to an instruction with a result
214       // type.
215       return false;
216     }
217     auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
218     assert(component_type);
219     if (component_type != matrix_type.element_type()) {
220       // The component's type does not match the matrix's column type.
221       return false;
222     }
223   }
224   return true;
225 }
226 
ComponentsForStructConstructionAreOK(opt::IRContext * ir_context,const opt::analysis::Struct & struct_type) const227 bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK(
228     opt::IRContext* ir_context,
229     const opt::analysis::Struct& struct_type) const {
230   if (static_cast<uint32_t>(message_.component().size()) !=
231       struct_type.element_types().size()) {
232     // The number of components must match the number of fields of the struct.
233     return false;
234   }
235   // Check that each component is the result id of an instruction those type
236   // matches the associated field type.
237   for (uint32_t field_index = 0;
238        field_index < struct_type.element_types().size(); field_index++) {
239     auto inst = ir_context->get_def_use_mgr()->GetDef(
240         message_.component()[field_index]);
241     if (inst == nullptr || !inst->type_id()) {
242       // The component does not correspond to an instruction with a result
243       // type.
244       return false;
245     }
246     auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
247     assert(component_type);
248     if (component_type != struct_type.element_types()[field_index]) {
249       // The component's type does not match the corresponding field type.
250       return false;
251     }
252   }
253   return true;
254 }
255 
ComponentsForVectorConstructionAreOK(opt::IRContext * ir_context,const opt::analysis::Vector & vector_type) const256 bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK(
257     opt::IRContext* ir_context,
258     const opt::analysis::Vector& vector_type) const {
259   uint32_t base_element_count = 0;
260   auto element_type = vector_type.element_type();
261   for (auto& component_id : message_.component()) {
262     auto inst = ir_context->get_def_use_mgr()->GetDef(component_id);
263     if (inst == nullptr || !inst->type_id()) {
264       // The component does not correspond to an instruction with a result
265       // type.
266       return false;
267     }
268     auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id());
269     assert(component_type);
270     if (component_type == element_type) {
271       base_element_count++;
272     } else if (component_type->AsVector() &&
273                component_type->AsVector()->element_type() == element_type) {
274       base_element_count += component_type->AsVector()->element_count();
275     } else {
276       // The component was not appropriate; e.g. no type corresponding to the
277       // given id was found, or the type that was found was not compatible
278       // with the vector being constructed.
279       return false;
280     }
281   }
282   // The number of components provided (when vector components are flattened
283   // out) needs to match the length of the vector being constructed.
284   return base_element_count == vector_type.element_count();
285 }
286 
ToMessage() const287 protobufs::Transformation TransformationCompositeConstruct::ToMessage() const {
288   protobufs::Transformation result;
289   *result.mutable_composite_construct() = message_;
290   return result;
291 }
292 
293 }  // namespace fuzz
294 }  // namespace spvtools
295