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_invert_comparison_operator.h"
16
17 #include <utility>
18
19 #include "source/fuzz/fuzzer_util.h"
20
21 namespace spvtools {
22 namespace fuzz {
23
TransformationInvertComparisonOperator(protobufs::TransformationInvertComparisonOperator message)24 TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
25 protobufs::TransformationInvertComparisonOperator message)
26 : message_(std::move(message)) {}
27
TransformationInvertComparisonOperator(uint32_t operator_id,uint32_t fresh_id)28 TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
29 uint32_t operator_id, uint32_t fresh_id) {
30 message_.set_operator_id(operator_id);
31 message_.set_fresh_id(fresh_id);
32 }
33
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const34 bool TransformationInvertComparisonOperator::IsApplicable(
35 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
36 // |message_.operator_id| must be valid and inversion must be supported for
37 // it.
38 auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
39 if (!inst || !IsInversionSupported(inst->opcode())) {
40 return false;
41 }
42
43 // Check that we can insert negation instruction.
44 auto* block = ir_context->get_instr_block(inst);
45 assert(block && "Instruction must have a basic block");
46
47 auto iter = fuzzerutil::GetIteratorForInstruction(block, inst);
48 ++iter;
49 assert(iter != block->end() && "Instruction can't be the last in the block");
50 assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
51 "Can't insert negation after comparison operator");
52
53 // |message_.fresh_id| must be fresh.
54 return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
55 }
56
Apply(opt::IRContext * ir_context,TransformationContext *) const57 void TransformationInvertComparisonOperator::Apply(
58 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
59 auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
60 assert(inst && "Result id of an operator is invalid");
61
62 // Insert negation after |inst|.
63 auto iter = fuzzerutil::GetIteratorForInstruction(
64 ir_context->get_instr_block(inst), inst);
65 ++iter;
66
67 iter.InsertBefore(MakeUnique<opt::Instruction>(
68 ir_context, SpvOpLogicalNot, inst->type_id(), inst->result_id(),
69 opt::Instruction::OperandList{
70 {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
71
72 // Change the result id of the original operator to |fresh_id|.
73 inst->SetResultId(message_.fresh_id());
74
75 // Invert the operator.
76 inst->SetOpcode(InvertOpcode(inst->opcode()));
77
78 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
79
80 ir_context->InvalidateAnalysesExceptFor(
81 opt::IRContext::Analysis::kAnalysisNone);
82 }
83
IsInversionSupported(SpvOp opcode)84 bool TransformationInvertComparisonOperator::IsInversionSupported(
85 SpvOp opcode) {
86 switch (opcode) {
87 case SpvOpSGreaterThan:
88 case SpvOpSGreaterThanEqual:
89 case SpvOpSLessThan:
90 case SpvOpSLessThanEqual:
91 case SpvOpUGreaterThan:
92 case SpvOpUGreaterThanEqual:
93 case SpvOpULessThan:
94 case SpvOpULessThanEqual:
95 case SpvOpIEqual:
96 case SpvOpINotEqual:
97 case SpvOpFOrdEqual:
98 case SpvOpFUnordEqual:
99 case SpvOpFOrdNotEqual:
100 case SpvOpFUnordNotEqual:
101 case SpvOpFOrdLessThan:
102 case SpvOpFUnordLessThan:
103 case SpvOpFOrdLessThanEqual:
104 case SpvOpFUnordLessThanEqual:
105 case SpvOpFOrdGreaterThan:
106 case SpvOpFUnordGreaterThan:
107 case SpvOpFOrdGreaterThanEqual:
108 case SpvOpFUnordGreaterThanEqual:
109 return true;
110 default:
111 return false;
112 }
113 }
114
InvertOpcode(SpvOp opcode)115 SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) {
116 assert(IsInversionSupported(opcode) && "Inversion must be supported");
117
118 switch (opcode) {
119 case SpvOpSGreaterThan:
120 return SpvOpSLessThanEqual;
121 case SpvOpSGreaterThanEqual:
122 return SpvOpSLessThan;
123 case SpvOpSLessThan:
124 return SpvOpSGreaterThanEqual;
125 case SpvOpSLessThanEqual:
126 return SpvOpSGreaterThan;
127 case SpvOpUGreaterThan:
128 return SpvOpULessThanEqual;
129 case SpvOpUGreaterThanEqual:
130 return SpvOpULessThan;
131 case SpvOpULessThan:
132 return SpvOpUGreaterThanEqual;
133 case SpvOpULessThanEqual:
134 return SpvOpUGreaterThan;
135 case SpvOpIEqual:
136 return SpvOpINotEqual;
137 case SpvOpINotEqual:
138 return SpvOpIEqual;
139 case SpvOpFOrdEqual:
140 return SpvOpFUnordNotEqual;
141 case SpvOpFUnordEqual:
142 return SpvOpFOrdNotEqual;
143 case SpvOpFOrdNotEqual:
144 return SpvOpFUnordEqual;
145 case SpvOpFUnordNotEqual:
146 return SpvOpFOrdEqual;
147 case SpvOpFOrdLessThan:
148 return SpvOpFUnordGreaterThanEqual;
149 case SpvOpFUnordLessThan:
150 return SpvOpFOrdGreaterThanEqual;
151 case SpvOpFOrdLessThanEqual:
152 return SpvOpFUnordGreaterThan;
153 case SpvOpFUnordLessThanEqual:
154 return SpvOpFOrdGreaterThan;
155 case SpvOpFOrdGreaterThan:
156 return SpvOpFUnordLessThanEqual;
157 case SpvOpFUnordGreaterThan:
158 return SpvOpFOrdLessThanEqual;
159 case SpvOpFOrdGreaterThanEqual:
160 return SpvOpFUnordLessThan;
161 case SpvOpFUnordGreaterThanEqual:
162 return SpvOpFOrdLessThan;
163 default:
164 // The program will fail in the debug mode because of the assertion
165 // at the beginning of the function.
166 return SpvOpNop;
167 }
168 }
169
ToMessage() const170 protobufs::Transformation TransformationInvertComparisonOperator::ToMessage()
171 const {
172 protobufs::Transformation result;
173 *result.mutable_invert_comparison_operator() = message_;
174 return result;
175 }
176
177 std::unordered_set<uint32_t>
GetFreshIds() const178 TransformationInvertComparisonOperator::GetFreshIds() const {
179 return {message_.fresh_id()};
180 }
181
182 } // namespace fuzz
183 } // namespace spvtools
184