1 // Copyright (c) 2020 Google LLC
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_replace_irrelevant_id.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/id_use_descriptor.h"
19
20 namespace spvtools {
21 namespace fuzz {
22
TransformationReplaceIrrelevantId(const protobufs::TransformationReplaceIrrelevantId & message)23 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
24 const protobufs::TransformationReplaceIrrelevantId& message)
25 : message_(message) {}
26
TransformationReplaceIrrelevantId(const protobufs::IdUseDescriptor & id_use_descriptor,uint32_t replacement_id)27 TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId(
28 const protobufs::IdUseDescriptor& id_use_descriptor,
29 uint32_t replacement_id) {
30 *message_.mutable_id_use_descriptor() = id_use_descriptor;
31 message_.set_replacement_id(replacement_id);
32 }
33
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const34 bool TransformationReplaceIrrelevantId::IsApplicable(
35 opt::IRContext* ir_context,
36 const TransformationContext& transformation_context) const {
37 auto id_of_interest = message_.id_use_descriptor().id_of_interest();
38
39 // The id must be irrelevant.
40 if (!transformation_context.GetFactManager()->IdIsIrrelevant(
41 id_of_interest)) {
42 return false;
43 }
44
45 // Find the instruction containing the id use, which must exist.
46 auto use_instruction =
47 FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
48 if (!use_instruction) {
49 return false;
50 }
51
52 // Check that the replacement id exists and retrieve its definition.
53 auto replacement_id_def =
54 ir_context->get_def_use_mgr()->GetDef(message_.replacement_id());
55 if (!replacement_id_def) {
56 return false;
57 }
58
59 // The type of the id of interest and of the replacement id must be the same.
60 uint32_t type_id_of_interest =
61 ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
62 uint32_t type_replacement_id = replacement_id_def->type_id();
63 if (type_id_of_interest != type_replacement_id) {
64 return false;
65 }
66
67 // The replacement id must not be the result of an OpFunction instruction.
68 if (replacement_id_def->opcode() == SpvOpFunction) {
69 return false;
70 }
71
72 // Consistency check: an irrelevant id cannot be a pointer.
73 assert(
74 !ir_context->get_type_mgr()->GetType(type_id_of_interest)->AsPointer() &&
75 "An irrelevant id cannot be a pointer");
76
77 uint32_t use_in_operand_index =
78 message_.id_use_descriptor().in_operand_index();
79
80 // The id use must be replaceable with any other id of the same type.
81 if (!fuzzerutil::IdUseCanBeReplaced(ir_context, transformation_context,
82 use_instruction, use_in_operand_index)) {
83 return false;
84 }
85
86 if (AttemptsToReplaceVariableInitializerWithNonConstant(
87 *use_instruction, *replacement_id_def)) {
88 return false;
89 }
90
91 // The id must be available to use at the use point.
92 return fuzzerutil::IdIsAvailableAtUse(
93 ir_context, use_instruction,
94 message_.id_use_descriptor().in_operand_index(),
95 message_.replacement_id());
96 }
97
Apply(opt::IRContext * ir_context,TransformationContext *) const98 void TransformationReplaceIrrelevantId::Apply(
99 opt::IRContext* ir_context,
100 TransformationContext* /* transformation_context */) const {
101 // Find the instruction.
102 auto instruction_to_change =
103 FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
104
105 // Replace the instruction.
106 instruction_to_change->SetInOperand(
107 message_.id_use_descriptor().in_operand_index(),
108 {message_.replacement_id()});
109
110 // Invalidate the analyses, since the usage of ids has been changed.
111 ir_context->InvalidateAnalysesExceptFor(
112 opt::IRContext::Analysis::kAnalysisNone);
113 }
114
ToMessage() const115 protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const {
116 protobufs::Transformation result;
117 *result.mutable_replace_irrelevant_id() = message_;
118 return result;
119 }
120
GetFreshIds() const121 std::unordered_set<uint32_t> TransformationReplaceIrrelevantId::GetFreshIds()
122 const {
123 return std::unordered_set<uint32_t>();
124 }
125
126 bool TransformationReplaceIrrelevantId::
AttemptsToReplaceVariableInitializerWithNonConstant(const opt::Instruction & use_instruction,const opt::Instruction & replacement_for_use)127 AttemptsToReplaceVariableInitializerWithNonConstant(
128 const opt::Instruction& use_instruction,
129 const opt::Instruction& replacement_for_use) {
130 return use_instruction.opcode() == SpvOpVariable &&
131 !spvOpcodeIsConstant(replacement_for_use.opcode());
132 }
133
134 } // namespace fuzz
135 } // namespace spvtools
136