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_equation_instruction.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19
20 namespace spvtools {
21 namespace fuzz {
22
TransformationEquationInstruction(const spvtools::fuzz::protobufs::TransformationEquationInstruction & message)23 TransformationEquationInstruction::TransformationEquationInstruction(
24 const spvtools::fuzz::protobufs::TransformationEquationInstruction& message)
25 : message_(message) {}
26
TransformationEquationInstruction(uint32_t fresh_id,SpvOp opcode,const std::vector<uint32_t> & in_operand_id,const protobufs::InstructionDescriptor & instruction_to_insert_before)27 TransformationEquationInstruction::TransformationEquationInstruction(
28 uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id,
29 const protobufs::InstructionDescriptor& instruction_to_insert_before) {
30 message_.set_fresh_id(fresh_id);
31 message_.set_opcode(opcode);
32 for (auto id : in_operand_id) {
33 message_.add_in_operand_id(id);
34 }
35 *message_.mutable_instruction_to_insert_before() =
36 instruction_to_insert_before;
37 }
38
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const39 bool TransformationEquationInstruction::IsApplicable(
40 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
41 // The result id must be fresh.
42 if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
43 return false;
44 }
45 // The instruction to insert before must exist.
46 auto insert_before =
47 FindInstruction(message_.instruction_to_insert_before(), ir_context);
48 if (!insert_before) {
49 return false;
50 }
51 // The input ids must all exist, not be OpUndef, and be available before this
52 // instruction.
53 for (auto id : message_.in_operand_id()) {
54 auto inst = ir_context->get_def_use_mgr()->GetDef(id);
55 if (!inst) {
56 return false;
57 }
58 if (inst->opcode() == SpvOpUndef) {
59 return false;
60 }
61 if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
62 id)) {
63 return false;
64 }
65 }
66
67 return MaybeGetResultType(ir_context) != 0;
68 }
69
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const70 void TransformationEquationInstruction::Apply(
71 opt::IRContext* ir_context,
72 TransformationContext* transformation_context) const {
73 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
74
75 opt::Instruction::OperandList in_operands;
76 std::vector<uint32_t> rhs_id;
77 for (auto id : message_.in_operand_id()) {
78 in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
79 rhs_id.push_back(id);
80 }
81
82 FindInstruction(message_.instruction_to_insert_before(), ir_context)
83 ->InsertBefore(MakeUnique<opt::Instruction>(
84 ir_context, static_cast<SpvOp>(message_.opcode()),
85 MaybeGetResultType(ir_context), message_.fresh_id(), in_operands));
86
87 ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
88
89 transformation_context->GetFactManager()->AddFactIdEquation(
90 message_.fresh_id(), static_cast<SpvOp>(message_.opcode()), rhs_id,
91 ir_context);
92 }
93
ToMessage() const94 protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
95 protobufs::Transformation result;
96 *result.mutable_equation_instruction() = message_;
97 return result;
98 }
99
MaybeGetResultType(opt::IRContext * ir_context) const100 uint32_t TransformationEquationInstruction::MaybeGetResultType(
101 opt::IRContext* ir_context) const {
102 switch (static_cast<SpvOp>(message_.opcode())) {
103 case SpvOpIAdd:
104 case SpvOpISub: {
105 if (message_.in_operand_id().size() != 2) {
106 return 0;
107 }
108 uint32_t first_operand_width = 0;
109 uint32_t first_operand_type_id = 0;
110 for (uint32_t index = 0; index < 2; index++) {
111 auto operand_inst = ir_context->get_def_use_mgr()->GetDef(
112 message_.in_operand_id(index));
113 if (!operand_inst || !operand_inst->type_id()) {
114 return 0;
115 }
116 auto operand_type =
117 ir_context->get_type_mgr()->GetType(operand_inst->type_id());
118 if (!(operand_type->AsInteger() ||
119 (operand_type->AsVector() &&
120 operand_type->AsVector()->element_type()->AsInteger()))) {
121 return 0;
122 }
123 uint32_t operand_width =
124 operand_type->AsInteger()
125 ? 1
126 : operand_type->AsVector()->element_count();
127 if (index == 0) {
128 first_operand_width = operand_width;
129 first_operand_type_id = operand_inst->type_id();
130 } else {
131 assert(first_operand_width != 0 &&
132 "The first operand should have been processed.");
133 if (operand_width != first_operand_width) {
134 return 0;
135 }
136 }
137 }
138 assert(first_operand_type_id != 0 &&
139 "A type must have been found for the first operand.");
140 return first_operand_type_id;
141 }
142 case SpvOpLogicalNot: {
143 if (message_.in_operand_id().size() != 1) {
144 return 0;
145 }
146 auto operand_inst =
147 ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
148 if (!operand_inst || !operand_inst->type_id()) {
149 return 0;
150 }
151 auto operand_type =
152 ir_context->get_type_mgr()->GetType(operand_inst->type_id());
153 if (!(operand_type->AsBool() ||
154 (operand_type->AsVector() &&
155 operand_type->AsVector()->element_type()->AsBool()))) {
156 return 0;
157 }
158 return operand_inst->type_id();
159 }
160 case SpvOpSNegate: {
161 if (message_.in_operand_id().size() != 1) {
162 return 0;
163 }
164 auto operand_inst =
165 ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
166 if (!operand_inst || !operand_inst->type_id()) {
167 return 0;
168 }
169 auto operand_type =
170 ir_context->get_type_mgr()->GetType(operand_inst->type_id());
171 if (!(operand_type->AsInteger() ||
172 (operand_type->AsVector() &&
173 operand_type->AsVector()->element_type()->AsInteger()))) {
174 return 0;
175 }
176 return operand_inst->type_id();
177 }
178
179 default:
180 assert(false && "Inappropriate opcode for equation instruction.");
181 return 0;
182 }
183 }
184
185 } // namespace fuzz
186 } // namespace spvtools
187