Roll external/abseil_cpp/ 8f739d18b..917bfee46 (2 commits) (#5887)
[KhronosGroup/SPIRV-Tools.git] / source / fuzz / transformation_add_parameter.cpp
blob8bd2ed7838ebaac6b2a4825d2ac8322a3c6c85c1
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.
15 #include "source/fuzz/transformation_add_parameter.h"
17 #include "source/fuzz/fuzzer_util.h"
19 namespace spvtools {
20 namespace fuzz {
22 TransformationAddParameter::TransformationAddParameter(
23 protobufs::TransformationAddParameter message)
24 : message_(std::move(message)) {}
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);
38 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;
48 // The type must be supported.
49 if (ir_context->get_def_use_mgr()->GetDef(message_.parameter_type_id()) ==
50 nullptr) {
51 return false;
53 if (!IsParameterTypeSupported(ir_context, message_.parameter_type_id())) {
54 return false;
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();
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;
69 uint32_t value_id = call_parameter_ids_map[caller_id];
71 auto value_instr = ir_context->get_def_use_mgr()->GetDef(value_id);
72 if (!value_instr) {
73 return false;
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;
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;
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;
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();
98 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");
105 std::map<uint32_t, uint32_t> call_parameter_ids_map =
106 fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
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.");
113 // Add new parameters to the function.
114 function->AddParameter(MakeUnique<opt::Instruction>(
115 ir_context, spv::Op::OpFunctionParameter, new_parameter_type_id,
116 message_.parameter_fresh_id(), opt::Instruction::OperandList()));
118 fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
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()]}});
126 // Update function's type.
128 // We use a separate scope here since |old_function_type| might become a
129 // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
131 const auto* old_function_type =
132 fuzzerutil::GetFunctionType(ir_context, function);
133 assert(old_function_type && "Function must have a valid type");
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));
141 parameter_type_ids.push_back(new_parameter_type_id);
143 fuzzerutil::UpdateFunctionType(
144 ir_context, function->result_id(), message_.function_type_fresh_id(),
145 old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
148 auto new_parameter_kind = new_parameter_type->kind();
150 // Make sure our changes are analyzed.
151 ir_context->InvalidateAnalysesExceptFor(
152 opt::IRContext::Analysis::kAnalysisNone);
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());
169 protobufs::Transformation TransformationAddParameter::ToMessage() const {
170 protobufs::Transformation result;
171 *result.mutable_add_parameter() = message_;
172 return result;
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 spv::Op::OpTypeBool:
182 case spv::Op::OpTypeInt:
183 case spv::Op::OpTypeFloat:
184 case spv::Op::OpTypeMatrix:
185 case spv::Op::OpTypeVector:
186 return true;
187 case spv::Op::OpTypeArray:
188 return IsParameterTypeSupported(ir_context,
189 type_inst->GetSingleWordInOperand(0));
190 case spv::Op::OpTypeStruct:
191 if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, type_id)) {
192 return false;
194 for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) {
195 if (!IsParameterTypeSupported(ir_context,
196 type_inst->GetSingleWordInOperand(i))) {
197 return false;
200 return true;
201 case spv::Op::OpTypePointer: {
202 spv::StorageClass storage_class =
203 static_cast<spv::StorageClass>(type_inst->GetSingleWordInOperand(0));
204 switch (storage_class) {
205 case spv::StorageClass::Private:
206 case spv::StorageClass::Function:
207 case spv::StorageClass::Workgroup: {
208 return IsParameterTypeSupported(ir_context,
209 type_inst->GetSingleWordInOperand(1));
211 default:
212 return false;
215 default:
216 return false;
220 std::unordered_set<uint32_t> TransformationAddParameter::GetFreshIds() const {
221 return {message_.parameter_fresh_id(), message_.function_type_fresh_id()};
224 } // namespace fuzz
225 } // namespace spvtools