• 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_add_parameter.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 
19 namespace spvtools {
20 namespace fuzz {
21 
TransformationAddParameter(protobufs::TransformationAddParameter message)22 TransformationAddParameter::TransformationAddParameter(
23     protobufs::TransformationAddParameter message)
24     : message_(std::move(message)) {}
25 
TransformationAddParameter(uint32_t function_id,uint32_t parameter_fresh_id,uint32_t parameter_type_id,std::map<uint32_t,uint32_t> call_parameter_ids,uint32_t function_type_fresh_id)26 TransformationAddParameter::TransformationAddParameter(
27     uint32_t function_id, uint32_t parameter_fresh_id,
28     uint32_t parameter_type_id, std::map<uint32_t, uint32_t> call_parameter_ids,
29     uint32_t function_type_fresh_id) {
30   message_.set_function_id(function_id);
31   message_.set_parameter_fresh_id(parameter_fresh_id);
32   message_.set_parameter_type_id(parameter_type_id);
33   *message_.mutable_call_parameter_ids() =
34       fuzzerutil::MapToRepeatedUInt32Pair(call_parameter_ids);
35   message_.set_function_type_fresh_id(function_type_fresh_id);
36 }
37 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const38 bool TransformationAddParameter::IsApplicable(
39     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
40   // Check that function exists.
41   const auto* function =
42       fuzzerutil::FindFunction(ir_context, message_.function_id());
43   if (!function ||
44       fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
45     return false;
46   }
47 
48   // The type must be supported.
49   if (ir_context->get_def_use_mgr()->GetDef(message_.parameter_type_id()) ==
50       nullptr) {
51     return false;
52   }
53   if (!IsParameterTypeSupported(ir_context, message_.parameter_type_id())) {
54     return false;
55   }
56 
57   // Iterate over all callers.
58   std::map<uint32_t, uint32_t> call_parameter_ids_map =
59       fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
60   for (auto* instr :
61        fuzzerutil::GetCallers(ir_context, message_.function_id())) {
62     uint32_t caller_id = instr->result_id();
63 
64     // If there is no entry for this caller, return false.
65     if (call_parameter_ids_map.find(caller_id) ==
66         call_parameter_ids_map.end()) {
67       return false;
68     }
69     uint32_t value_id = call_parameter_ids_map[caller_id];
70 
71     auto value_instr = ir_context->get_def_use_mgr()->GetDef(value_id);
72     if (!value_instr) {
73       return false;
74     }
75     // If the id of the value of the map is not available before the caller,
76     // return false.
77     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, instr,
78                                                     value_id)) {
79       return false;
80     }
81 
82     // The type of the value must be defined.
83     uint32_t value_type_id = fuzzerutil::GetTypeId(ir_context, value_id);
84     if (!value_type_id) {
85       return false;
86     }
87 
88     // Type of every value of the map must be the same for all callers.
89     if (message_.parameter_type_id() != value_type_id) {
90       return false;
91     }
92   }
93   return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) &&
94          fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
95          message_.parameter_fresh_id() != message_.function_type_fresh_id();
96 }
97 
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const98 void TransformationAddParameter::Apply(
99     opt::IRContext* ir_context,
100     TransformationContext* transformation_context) const {
101   // Find the function that will be transformed.
102   auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
103   assert(function && "Can't find the function");
104 
105   std::map<uint32_t, uint32_t> call_parameter_ids_map =
106       fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
107 
108   uint32_t new_parameter_type_id = message_.parameter_type_id();
109   auto new_parameter_type =
110       ir_context->get_type_mgr()->GetType(new_parameter_type_id);
111   assert(new_parameter_type && "New parameter has invalid type.");
112 
113   // Add new parameters to the function.
114   function->AddParameter(MakeUnique<opt::Instruction>(
115       ir_context, SpvOpFunctionParameter, new_parameter_type_id,
116       message_.parameter_fresh_id(), opt::Instruction::OperandList()));
117 
118   fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
119 
120   // Fix all OpFunctionCall instructions.
121   for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
122     inst->AddOperand(
123         {SPV_OPERAND_TYPE_ID, {call_parameter_ids_map[inst->result_id()]}});
124   }
125 
126   // Update function's type.
127   {
128     // We use a separate scope here since |old_function_type| might become a
129     // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
130 
131     const auto* old_function_type =
132         fuzzerutil::GetFunctionType(ir_context, function);
133     assert(old_function_type && "Function must have a valid type");
134 
135     std::vector<uint32_t> parameter_type_ids;
136     for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
137       parameter_type_ids.push_back(
138           old_function_type->GetSingleWordInOperand(i));
139     }
140 
141     parameter_type_ids.push_back(new_parameter_type_id);
142 
143     fuzzerutil::UpdateFunctionType(
144         ir_context, function->result_id(), message_.function_type_fresh_id(),
145         old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
146   }
147 
148   auto new_parameter_kind = new_parameter_type->kind();
149 
150   // Make sure our changes are analyzed.
151   ir_context->InvalidateAnalysesExceptFor(
152       opt::IRContext::Analysis::kAnalysisNone);
153 
154   // If the |new_parameter_type_id| is not a pointer type, mark id as
155   // irrelevant so that we can replace its use with some other id. If the
156   // |new_parameter_type_id| is a pointer type, we cannot mark it with
157   // IdIsIrrelevant, because this pointer might be replaced by a pointer from
158   // original shader. This would change the semantics of the module. In the case
159   // of a pointer type we mark it with PointeeValueIsIrrelevant.
160   if (new_parameter_kind != opt::analysis::Type::kPointer) {
161     transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
162         message_.parameter_fresh_id());
163   } else {
164     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
165         message_.parameter_fresh_id());
166   }
167 }
168 
ToMessage() const169 protobufs::Transformation TransformationAddParameter::ToMessage() const {
170   protobufs::Transformation result;
171   *result.mutable_add_parameter() = message_;
172   return result;
173 }
174 
IsParameterTypeSupported(opt::IRContext * ir_context,uint32_t type_id)175 bool TransformationAddParameter::IsParameterTypeSupported(
176     opt::IRContext* ir_context, uint32_t type_id) {
177   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
178   //  Think about other type instructions we can add here.
179   opt::Instruction* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
180   switch (type_inst->opcode()) {
181     case SpvOpTypeBool:
182     case SpvOpTypeInt:
183     case SpvOpTypeFloat:
184     case SpvOpTypeMatrix:
185     case SpvOpTypeVector:
186       return true;
187     case SpvOpTypeArray:
188       return IsParameterTypeSupported(ir_context,
189                                       type_inst->GetSingleWordInOperand(0));
190     case SpvOpTypeStruct:
191       if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, type_id)) {
192         return false;
193       }
194       for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) {
195         if (!IsParameterTypeSupported(ir_context,
196                                       type_inst->GetSingleWordInOperand(i))) {
197           return false;
198         }
199       }
200       return true;
201     case SpvOpTypePointer: {
202       SpvStorageClass storage_class =
203           static_cast<SpvStorageClass>(type_inst->GetSingleWordInOperand(0));
204       switch (storage_class) {
205         case SpvStorageClassPrivate:
206         case SpvStorageClassFunction:
207         case SpvStorageClassWorkgroup: {
208           return IsParameterTypeSupported(ir_context,
209                                           type_inst->GetSingleWordInOperand(1));
210         }
211         default:
212           return false;
213       }
214     }
215     default:
216       return false;
217   }
218 }
219 
GetFreshIds() const220 std::unordered_set<uint32_t> TransformationAddParameter::GetFreshIds() const {
221   return {message_.parameter_fresh_id(), message_.function_type_fresh_id()};
222 }
223 
224 }  // namespace fuzz
225 }  // namespace spvtools
226