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