1 // Copyright (c) 2021 Shiyu Liu
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/fuzzer_pass_wrap_vector_synonym.h"
16 #include "source/fuzz/fuzzer_context.h"
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/transformation_composite_construct.h"
19 #include "source/fuzz/transformation_wrap_vector_synonym.h"
20 
21 namespace spvtools {
22 namespace fuzz {
23 
FuzzerPassWrapVectorSynonym(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations,bool ignore_inapplicable_transformations)24 FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym(
25     opt::IRContext* ir_context, TransformationContext* transformation_context,
26     FuzzerContext* fuzzer_context,
27     protobufs::TransformationSequence* transformations,
28     bool ignore_inapplicable_transformations)
29     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
30                  transformations, ignore_inapplicable_transformations) {}
31 
Apply()32 void FuzzerPassWrapVectorSynonym::Apply() {
33   ForEachInstructionWithInstructionDescriptor(
34       [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
35              opt::BasicBlock::iterator instruction_iterator,
36              const protobufs::InstructionDescriptor& instruction_descriptor)
37           -> void {
38 
39         // Randomly decide whether to wrap it to a vector operation.
40         if (!GetFuzzerContext()->ChoosePercentage(
41                 GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) {
42           return;
43         }
44 
45         // The transformation is not applicable if the instruction has missing
46         // result id, type id, or is not supported type.
47         if (!TransformationWrapVectorSynonym::IsInstructionSupported(
48                 GetIRContext(), *instruction_iterator)) {
49           return;
50         }
51 
52         // It must be valid to insert an OpCompositeConstruct instruction
53         // before |instruction_iterator|.
54         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
55                 SpvOpCompositeConstruct, instruction_iterator)) {
56           return;
57         }
58 
59         // Get the scalar operands from the original instruction.
60         opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef(
61             instruction_iterator->GetSingleWordInOperand(0));
62         opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef(
63             instruction_iterator->GetSingleWordInOperand(1));
64 
65         // We need to be able to make a synonym of the scalar operation's result
66         // id, as well as the operand ids (for example, they cannot be
67         // irrelevant).
68         if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(),
69                                           *GetTransformationContext(),
70                                           *instruction_iterator)) {
71           return;
72         }
73         if (!fuzzerutil::CanMakeSynonymOf(
74                 GetIRContext(), *GetTransformationContext(), *operand1)) {
75           return;
76         }
77         if (!fuzzerutil::CanMakeSynonymOf(
78                 GetIRContext(), *GetTransformationContext(), *operand2)) {
79           return;
80         }
81 
82         // Get a random vector size from 2 to 4.
83         uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector();
84 
85         // Randomly choose a position that target ids should be placed at.
86         // The position is in range [0, n - 1], where n is the size of the
87         // vector.
88         uint32_t position =
89             GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size);
90 
91         // Stores the ids of scalar constants.
92         std::vector<uint32_t> vec1_components;
93         std::vector<uint32_t> vec2_components;
94 
95         // Populate components based on vector type and size.
96         for (uint32_t i = 0; i < vector_size; ++i) {
97           if (i == position) {
98             vec1_components.emplace_back(operand1->result_id());
99             vec2_components.emplace_back(operand2->result_id());
100           } else {
101             vec1_components.emplace_back(
102                 FindOrCreateZeroConstant(operand1->type_id(), true));
103             vec2_components.emplace_back(
104                 FindOrCreateZeroConstant(operand2->type_id(), true));
105           }
106         }
107 
108         // Add two OpCompositeConstruct to the module with result id returned.
109         // The added vectors may have different types, for instance if the
110         // scalar instruction operates on integers with differing sign.
111 
112         // Add the first OpCompositeConstruct that wraps the id of the first
113         // operand.
114         uint32_t result_id1 = GetFuzzerContext()->GetFreshId();
115         ApplyTransformation(TransformationCompositeConstruct(
116             FindOrCreateVectorType(operand1->type_id(), vector_size),
117             vec1_components, instruction_descriptor, result_id1));
118 
119         // Add the second OpCompositeConstruct that wraps the id of the second
120         // operand.
121         uint32_t result_id2 = GetFuzzerContext()->GetFreshId();
122         ApplyTransformation(TransformationCompositeConstruct(
123             FindOrCreateVectorType(operand2->type_id(), vector_size),
124             vec2_components, instruction_descriptor, result_id2));
125 
126         // The result of the vector instruction that
127         // TransformationWrapVectorSynonym will create should be a vector of the
128         // right size, with the scalar instruction's result type as its element
129         // type. This can be distinct from the types of the operands, if the
130         // scalar instruction adds two signed integers and stores the result in
131         // an unsigned id, for example. A transformation is applied to add the
132         // right type to the module.
133         FindOrCreateVectorType(instruction_iterator->type_id(), vector_size);
134 
135         // Apply transformation to do vector operation and add synonym between
136         // the result vector id and the id of the original instruction.
137         ApplyTransformation(TransformationWrapVectorSynonym(
138             instruction_iterator->result_id(), result_id1, result_id2,
139             GetFuzzerContext()->GetFreshId(), position));
140       });
141 }
142 
143 }  // namespace fuzz
144 }  // namespace spvtools
145