• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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