1 // Copyright (c) 2020 Vasyl Teliman
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_add_parameter.h"
17 #include "source/fuzz/fuzzer_util.h"
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());
44 fuzzerutil::FunctionIsEntryPoint(ir_context
, function
->result_id())) {
48 // The type must be supported.
49 if (ir_context
->get_def_use_mgr()->GetDef(message_
.parameter_type_id()) ==
53 if (!IsParameterTypeSupported(ir_context
, message_
.parameter_type_id())) {
57 // Iterate over all callers.
58 std::map
<uint32_t, uint32_t> call_parameter_ids_map
=
59 fuzzerutil::RepeatedUInt32PairToMap(message_
.call_parameter_ids());
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()) {
69 uint32_t value_id
= call_parameter_ids_map
[caller_id
];
71 auto value_instr
= ir_context
->get_def_use_mgr()->GetDef(value_id
);
75 // If the id of the value of the map is not available before the caller,
77 if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context
, instr
,
82 // The type of the value must be defined.
83 uint32_t value_type_id
= fuzzerutil::GetTypeId(ir_context
, value_id
);
88 // Type of every value of the map must be the same for all callers.
89 if (message_
.parameter_type_id() != value_type_id
) {
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())) {
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());
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_
;
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
:
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
)) {
194 for (uint32_t i
= 0; i
< type_inst
->NumInOperands(); i
++) {
195 if (!IsParameterTypeSupported(ir_context
,
196 type_inst
->GetSingleWordInOperand(i
))) {
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));
220 std::unordered_set
<uint32_t> TransformationAddParameter::GetFreshIds() const {
221 return {message_
.parameter_fresh_id(), message_
.function_type_fresh_id()};
225 } // namespace spvtools