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