• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Vasyl Teliman
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_permute_function_parameters.h"
16 
17 #include <vector>
18 
19 #include "source/fuzz/fuzzer_util.h"
20 
21 namespace spvtools {
22 namespace fuzz {
23 
24 TransformationPermuteFunctionParameters::
TransformationPermuteFunctionParameters(protobufs::TransformationPermuteFunctionParameters message)25     TransformationPermuteFunctionParameters(
26         protobufs::TransformationPermuteFunctionParameters message)
27     : message_(std::move(message)) {}
28 
29 TransformationPermuteFunctionParameters::
TransformationPermuteFunctionParameters(uint32_t function_id,uint32_t function_type_fresh_id,const std::vector<uint32_t> & permutation)30     TransformationPermuteFunctionParameters(
31         uint32_t function_id, uint32_t function_type_fresh_id,
32         const std::vector<uint32_t>& permutation) {
33   message_.set_function_id(function_id);
34   message_.set_function_type_fresh_id(function_type_fresh_id);
35 
36   for (auto index : permutation) {
37     message_.add_permutation(index);
38   }
39 }
40 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const41 bool TransformationPermuteFunctionParameters::IsApplicable(
42     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
43   // Check that function exists
44   const auto* function =
45       fuzzerutil::FindFunction(ir_context, message_.function_id());
46   if (!function || function->DefInst().opcode() != SpvOpFunction ||
47       fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
48     return false;
49   }
50 
51   // Check that permutation has valid indices
52   const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
53   assert(function_type && "Function type is null");
54 
55   std::vector<uint32_t> permutation(message_.permutation().begin(),
56                                     message_.permutation().end());
57 
58   // Don't take return type into account
59   auto arg_size = function_type->NumInOperands() - 1;
60 
61   // |permutation| vector should be equal to the number of arguments
62   if (static_cast<uint32_t>(permutation.size()) != arg_size) {
63     return false;
64   }
65 
66   // Check that permutation doesn't have duplicated values.
67   assert(!fuzzerutil::HasDuplicates(permutation) &&
68          "Permutation has duplicates");
69 
70   // Check that elements in permutation are in range [0, arg_size - 1].
71   //
72   // We must check whether the permutation is empty first because in that case
73   // |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
74   // it's an unsigned integer.
75   if (!permutation.empty() &&
76       !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
77     return false;
78   }
79 
80   return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id());
81 }
82 
Apply(opt::IRContext * ir_context,TransformationContext *) const83 void TransformationPermuteFunctionParameters::Apply(
84     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
85   // Find the function that will be transformed
86   auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
87   assert(function && "Can't find the function");
88 
89   // Adjust OpFunctionParameter instructions
90 
91   // Collect ids and types from OpFunctionParameter instructions
92   std::vector<uint32_t> param_id, param_type;
93   function->ForEachParam(
94       [&param_id, &param_type](const opt::Instruction* param) {
95         param_id.push_back(param->result_id());
96         param_type.push_back(param->type_id());
97       });
98 
99   // Permute parameters' ids and types
100   std::vector<uint32_t> permuted_param_id, permuted_param_type;
101   for (auto index : message_.permutation()) {
102     permuted_param_id.push_back(param_id[index]);
103     permuted_param_type.push_back(param_type[index]);
104   }
105 
106   // Set OpFunctionParameter instructions to point to new parameters
107   size_t i = 0;
108   function->ForEachParam(
109       [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) {
110         param->SetResultType(permuted_param_type[i]);
111         param->SetResultId(permuted_param_id[i]);
112         ++i;
113       });
114 
115   // Fix all OpFunctionCall instructions
116   for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) {
117     opt::Instruction::OperandList call_operands = {
118         call->GetInOperand(0)  // Function id
119     };
120 
121     for (auto index : message_.permutation()) {
122       // Take function id into account
123       call_operands.push_back(call->GetInOperand(index + 1));
124     }
125 
126     call->SetInOperands(std::move(call_operands));
127   }
128 
129   // Update function type.
130   {
131     // We use a separate scope here since |old_function_type_inst| might become
132     // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
133 
134     auto* old_function_type_inst =
135         fuzzerutil::GetFunctionType(ir_context, function);
136     assert(old_function_type_inst && "Function must have a valid type");
137 
138     std::vector<uint32_t> parameter_type_ids;
139     for (auto index : message_.permutation()) {
140       // +1 since the first operand to OpTypeFunction is a return type.
141       parameter_type_ids.push_back(
142           old_function_type_inst->GetSingleWordInOperand(index + 1));
143     }
144 
145     // Change function's type.
146     fuzzerutil::UpdateFunctionType(
147         ir_context, function->result_id(), message_.function_type_fresh_id(),
148         old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids);
149   }
150 
151   // Make sure our changes are analyzed
152   ir_context->InvalidateAnalysesExceptFor(
153       opt::IRContext::Analysis::kAnalysisNone);
154 }
155 
ToMessage() const156 protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage()
157     const {
158   protobufs::Transformation result;
159   *result.mutable_permute_function_parameters() = message_;
160   return result;
161 }
162 
163 std::unordered_set<uint32_t>
GetFreshIds() const164 TransformationPermuteFunctionParameters::GetFreshIds() const {
165   return {message_.function_type_fresh_id()};
166 }
167 
168 }  // namespace fuzz
169 }  // namespace spvtools
170