1 // Copyright (c) 2019 Google LLC
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "source/fuzz/transformation_vector_shuffle.h"
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
23 TransformationVectorShuffle::TransformationVectorShuffle(
24 protobufs::TransformationVectorShuffle message
)
25 : message_(std::move(message
)) {}
27 TransformationVectorShuffle::TransformationVectorShuffle(
28 const protobufs::InstructionDescriptor
& instruction_to_insert_before
,
29 uint32_t fresh_id
, uint32_t vector1
, uint32_t vector2
,
30 const std::vector
<uint32_t>& component
) {
31 *message_
.mutable_instruction_to_insert_before() =
32 instruction_to_insert_before
;
33 message_
.set_fresh_id(fresh_id
);
34 message_
.set_vector1(vector1
);
35 message_
.set_vector2(vector2
);
36 for (auto a_component
: component
) {
37 message_
.add_component(a_component
);
41 bool TransformationVectorShuffle::IsApplicable(
42 opt::IRContext
* ir_context
, const TransformationContext
& /*unused*/) const {
43 // The fresh id must not already be in use.
44 if (!fuzzerutil::IsFreshId(ir_context
, message_
.fresh_id())) {
47 // The instruction before which the shuffle will be inserted must exist.
48 auto instruction_to_insert_before
=
49 FindInstruction(message_
.instruction_to_insert_before(), ir_context
);
50 if (!instruction_to_insert_before
) {
53 // The first vector must be an instruction with a type id
54 auto vector1_instruction
=
55 ir_context
->get_def_use_mgr()->GetDef(message_
.vector1());
56 if (!vector1_instruction
|| !vector1_instruction
->type_id()) {
59 // The second vector must be an instruction with a type id
60 auto vector2_instruction
=
61 ir_context
->get_def_use_mgr()->GetDef(message_
.vector2());
62 if (!vector2_instruction
|| !vector2_instruction
->type_id()) {
66 ir_context
->get_type_mgr()->GetType(vector1_instruction
->type_id());
67 // The first vector instruction's type must actually be a vector type.
68 if (!vector1_type
->AsVector()) {
72 ir_context
->get_type_mgr()->GetType(vector2_instruction
->type_id());
73 // The second vector instruction's type must actually be a vector type.
74 if (!vector2_type
->AsVector()) {
77 // The element types of the vectors must be the same.
78 if (vector1_type
->AsVector()->element_type() !=
79 vector2_type
->AsVector()->element_type()) {
82 uint32_t combined_size
= vector1_type
->AsVector()->element_count() +
83 vector2_type
->AsVector()->element_count();
84 for (auto a_compoment
: message_
.component()) {
85 // 0xFFFFFFFF is used to represent an undefined component. Unless
86 // undefined, a component must be less than the combined size of the
88 if (a_compoment
!= 0xFFFFFFFF && a_compoment
>= combined_size
) {
92 // The module must already declare an appropriate type in which to store the
93 // result of the shuffle.
94 if (!GetResultTypeId(ir_context
, *vector1_type
->AsVector()->element_type())) {
97 // Each of the vectors used in the shuffle must be available at the insertion
99 for (auto used_instruction
: {vector1_instruction
, vector2_instruction
}) {
100 if (auto block
= ir_context
->get_instr_block(used_instruction
)) {
101 if (!ir_context
->GetDominatorAnalysis(block
->GetParent())
102 ->Dominates(used_instruction
, instruction_to_insert_before
)) {
108 // It must be legitimate to insert an OpVectorShuffle before the identified
110 return fuzzerutil::CanInsertOpcodeBeforeInstruction(
111 spv::Op::OpVectorShuffle
, instruction_to_insert_before
);
114 void TransformationVectorShuffle::Apply(
115 opt::IRContext
* ir_context
,
116 TransformationContext
* transformation_context
) const {
117 // Make input operands for a shuffle instruction - these comprise the two
118 // vectors being shuffled, followed by the integer literal components.
119 opt::Instruction::OperandList shuffle_operands
= {
120 {SPV_OPERAND_TYPE_ID
, {message_
.vector1()}},
121 {SPV_OPERAND_TYPE_ID
, {message_
.vector2()}}};
122 for (auto a_component
: message_
.component()) {
123 shuffle_operands
.push_back(
124 {SPV_OPERAND_TYPE_LITERAL_INTEGER
, {a_component
}});
127 uint32_t result_type_id
= GetResultTypeId(
129 *GetVectorType(ir_context
, message_
.vector1())->element_type());
131 // Add a shuffle instruction right before the instruction identified by
132 // |message_.instruction_to_insert_before|.
134 FindInstruction(message_
.instruction_to_insert_before(), ir_context
);
135 opt::Instruction
* new_instruction
=
136 insert_before
->InsertBefore(MakeUnique
<opt::Instruction
>(
137 ir_context
, spv::Op::OpVectorShuffle
, result_type_id
,
138 message_
.fresh_id(), shuffle_operands
));
139 fuzzerutil::UpdateModuleIdBound(ir_context
, message_
.fresh_id());
140 // Inform the def-use manager about the new instruction and record its basic
142 ir_context
->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction
);
143 ir_context
->set_instr_block(new_instruction
,
144 ir_context
->get_instr_block(insert_before
));
146 AddDataSynonymFacts(ir_context
, transformation_context
);
149 protobufs::Transformation
TransformationVectorShuffle::ToMessage() const {
150 protobufs::Transformation result
;
151 *result
.mutable_vector_shuffle() = message_
;
155 uint32_t TransformationVectorShuffle::GetResultTypeId(
156 opt::IRContext
* ir_context
, const opt::analysis::Type
& element_type
) const {
157 opt::analysis::Vector
result_type(
158 &element_type
, static_cast<uint32_t>(message_
.component_size()));
159 return ir_context
->get_type_mgr()->GetId(&result_type
);
162 opt::analysis::Vector
* TransformationVectorShuffle::GetVectorType(
163 opt::IRContext
* ir_context
, uint32_t id_of_vector
) {
164 return ir_context
->get_type_mgr()
165 ->GetType(ir_context
->get_def_use_mgr()->GetDef(id_of_vector
)->type_id())
169 std::unordered_set
<uint32_t> TransformationVectorShuffle::GetFreshIds() const {
170 return {message_
.fresh_id()};
173 void TransformationVectorShuffle::AddDataSynonymFacts(
174 opt::IRContext
* ir_context
,
175 TransformationContext
* transformation_context
) const {
176 // If the new instruction is irrelevant (because it is in a dead block), it
177 // cannot participate in any DataSynonym fact.
178 if (transformation_context
->GetFactManager()->IdIsIrrelevant(
179 message_
.fresh_id())) {
183 // Add synonym facts relating the defined elements of the shuffle result to
184 // the vector components that they come from.
185 for (uint32_t component_index
= 0;
186 component_index
< static_cast<uint32_t>(message_
.component_size());
188 uint32_t component
= message_
.component(component_index
);
189 if (component
== 0xFFFFFFFF) {
190 // This component is undefined, we do not introduce a synonym.
193 // This describes the element of the result vector associated with
194 // |component_index|.
195 protobufs::DataDescriptor descriptor_for_result_component
=
196 MakeDataDescriptor(message_
.fresh_id(), {component_index
});
198 protobufs::DataDescriptor descriptor_for_source_component
;
200 // Get a data descriptor for the component of the input vector to which
201 // |component| refers.
203 GetVectorType(ir_context
, message_
.vector1())->element_count()) {
204 // Check that the first vector can participate in data synonym facts.
205 if (!fuzzerutil::CanMakeSynonymOf(
206 ir_context
, *transformation_context
,
207 *ir_context
->get_def_use_mgr()->GetDef(message_
.vector1()))) {
210 descriptor_for_source_component
=
211 MakeDataDescriptor(message_
.vector1(), {component
});
213 // Check that the second vector can participate in data synonym facts.
214 if (!fuzzerutil::CanMakeSynonymOf(
215 ir_context
, *transformation_context
,
216 *ir_context
->get_def_use_mgr()->GetDef(message_
.vector2()))) {
219 auto index_into_vector_2
=
221 GetVectorType(ir_context
, message_
.vector1())->element_count();
223 index_into_vector_2
<
224 GetVectorType(ir_context
, message_
.vector2())->element_count() &&
225 "Vector shuffle index is out of bounds.");
226 descriptor_for_source_component
=
227 MakeDataDescriptor(message_
.vector2(), {index_into_vector_2
});
230 // Add a fact relating this input vector component with the associated
232 transformation_context
->GetFactManager()->AddFactDataSynonym(
233 descriptor_for_result_component
, descriptor_for_source_component
);
238 } // namespace spvtools