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(spv::Op::OpLogicalNot,
51 iter) &&
52 "Can't insert negation after comparison operator");
53
54 // |message_.fresh_id| must be fresh.
55 return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
56 }
57
Apply(opt::IRContext * ir_context,TransformationContext *) const58 void TransformationInvertComparisonOperator::Apply(
59 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
60 auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
61 assert(inst && "Result id of an operator is invalid");
62
63 // Insert negation after |inst|.
64 auto iter = fuzzerutil::GetIteratorForInstruction(
65 ir_context->get_instr_block(inst), inst);
66 ++iter;
67
68 iter.InsertBefore(MakeUnique<opt::Instruction>(
69 ir_context, spv::Op::OpLogicalNot, inst->type_id(), inst->result_id(),
70 opt::Instruction::OperandList{
71 {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
72
73 // Change the result id of the original operator to |fresh_id|.
74 inst->SetResultId(message_.fresh_id());
75
76 // Invert the operator.
77 inst->SetOpcode(InvertOpcode(inst->opcode()));
78
79 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
80
81 ir_context->InvalidateAnalysesExceptFor(
82 opt::IRContext::Analysis::kAnalysisNone);
83 }
84
IsInversionSupported(spv::Op opcode)85 bool TransformationInvertComparisonOperator::IsInversionSupported(
86 spv::Op opcode) {
87 switch (opcode) {
88 case spv::Op::OpSGreaterThan:
89 case spv::Op::OpSGreaterThanEqual:
90 case spv::Op::OpSLessThan:
91 case spv::Op::OpSLessThanEqual:
92 case spv::Op::OpUGreaterThan:
93 case spv::Op::OpUGreaterThanEqual:
94 case spv::Op::OpULessThan:
95 case spv::Op::OpULessThanEqual:
96 case spv::Op::OpIEqual:
97 case spv::Op::OpINotEqual:
98 case spv::Op::OpFOrdEqual:
99 case spv::Op::OpFUnordEqual:
100 case spv::Op::OpFOrdNotEqual:
101 case spv::Op::OpFUnordNotEqual:
102 case spv::Op::OpFOrdLessThan:
103 case spv::Op::OpFUnordLessThan:
104 case spv::Op::OpFOrdLessThanEqual:
105 case spv::Op::OpFUnordLessThanEqual:
106 case spv::Op::OpFOrdGreaterThan:
107 case spv::Op::OpFUnordGreaterThan:
108 case spv::Op::OpFOrdGreaterThanEqual:
109 case spv::Op::OpFUnordGreaterThanEqual:
110 return true;
111 default:
112 return false;
113 }
114 }
115
InvertOpcode(spv::Op opcode)116 spv::Op TransformationInvertComparisonOperator::InvertOpcode(spv::Op opcode) {
117 assert(IsInversionSupported(opcode) && "Inversion must be supported");
118
119 switch (opcode) {
120 case spv::Op::OpSGreaterThan:
121 return spv::Op::OpSLessThanEqual;
122 case spv::Op::OpSGreaterThanEqual:
123 return spv::Op::OpSLessThan;
124 case spv::Op::OpSLessThan:
125 return spv::Op::OpSGreaterThanEqual;
126 case spv::Op::OpSLessThanEqual:
127 return spv::Op::OpSGreaterThan;
128 case spv::Op::OpUGreaterThan:
129 return spv::Op::OpULessThanEqual;
130 case spv::Op::OpUGreaterThanEqual:
131 return spv::Op::OpULessThan;
132 case spv::Op::OpULessThan:
133 return spv::Op::OpUGreaterThanEqual;
134 case spv::Op::OpULessThanEqual:
135 return spv::Op::OpUGreaterThan;
136 case spv::Op::OpIEqual:
137 return spv::Op::OpINotEqual;
138 case spv::Op::OpINotEqual:
139 return spv::Op::OpIEqual;
140 case spv::Op::OpFOrdEqual:
141 return spv::Op::OpFUnordNotEqual;
142 case spv::Op::OpFUnordEqual:
143 return spv::Op::OpFOrdNotEqual;
144 case spv::Op::OpFOrdNotEqual:
145 return spv::Op::OpFUnordEqual;
146 case spv::Op::OpFUnordNotEqual:
147 return spv::Op::OpFOrdEqual;
148 case spv::Op::OpFOrdLessThan:
149 return spv::Op::OpFUnordGreaterThanEqual;
150 case spv::Op::OpFUnordLessThan:
151 return spv::Op::OpFOrdGreaterThanEqual;
152 case spv::Op::OpFOrdLessThanEqual:
153 return spv::Op::OpFUnordGreaterThan;
154 case spv::Op::OpFUnordLessThanEqual:
155 return spv::Op::OpFOrdGreaterThan;
156 case spv::Op::OpFOrdGreaterThan:
157 return spv::Op::OpFUnordLessThanEqual;
158 case spv::Op::OpFUnordGreaterThan:
159 return spv::Op::OpFOrdLessThanEqual;
160 case spv::Op::OpFOrdGreaterThanEqual:
161 return spv::Op::OpFUnordLessThan;
162 case spv::Op::OpFUnordGreaterThanEqual:
163 return spv::Op::OpFOrdLessThan;
164 default:
165 // The program will fail in the debug mode because of the assertion
166 // at the beginning of the function.
167 return spv::Op::OpNop;
168 }
169 }
170
ToMessage() const171 protobufs::Transformation TransformationInvertComparisonOperator::ToMessage()
172 const {
173 protobufs::Transformation result;
174 *result.mutable_invert_comparison_operator() = message_;
175 return result;
176 }
177
178 std::unordered_set<uint32_t>
GetFreshIds() const179 TransformationInvertComparisonOperator::GetFreshIds() const {
180 return {message_.fresh_id()};
181 }
182
183 } // namespace fuzz
184 } // namespace spvtools
185