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_swap_conditional_branch_operands.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
23 TransformationSwapConditionalBranchOperands::
TransformationSwapConditionalBranchOperands(const spvtools::fuzz::protobufs::TransformationSwapConditionalBranchOperands & message)24     TransformationSwapConditionalBranchOperands(
25         const spvtools::fuzz::protobufs::
26             TransformationSwapConditionalBranchOperands& message)
27     : message_(message) {}
28 
29 TransformationSwapConditionalBranchOperands::
TransformationSwapConditionalBranchOperands(const protobufs::InstructionDescriptor & instruction_descriptor,uint32_t fresh_id)30     TransformationSwapConditionalBranchOperands(
31         const protobufs::InstructionDescriptor& instruction_descriptor,
32         uint32_t fresh_id) {
33   *message_.mutable_instruction_descriptor() = instruction_descriptor;
34   message_.set_fresh_id(fresh_id);
35 }
36 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const37 bool TransformationSwapConditionalBranchOperands::IsApplicable(
38     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
39   const auto* inst =
40       FindInstruction(message_.instruction_descriptor(), ir_context);
41   return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
42          inst->opcode() == SpvOpBranchConditional;
43 }
44 
Apply(opt::IRContext * ir_context,TransformationContext *) const45 void TransformationSwapConditionalBranchOperands::Apply(
46     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
47   auto* branch_inst =
48       FindInstruction(message_.instruction_descriptor(), ir_context);
49   assert(branch_inst);
50 
51   auto* block = ir_context->get_instr_block(branch_inst);
52   assert(block);
53 
54   // Compute the last instruction in the |block| that allows us to insert
55   // OpLogicalNot above it.
56   auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
57   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
58     // There might be a merge instruction before OpBranchConditional.
59     --iter;
60   }
61 
62   assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
63          "We should now be able to insert SpvOpLogicalNot before |iter|");
64 
65   // Get the instruction whose result is used as a condition for
66   // OpBranchConditional.
67   const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
68       branch_inst->GetSingleWordInOperand(0));
69   assert(condition_inst);
70 
71   // We are swapping the labels in OpBranchConditional. This means that we must
72   // invert the guard as well. We are using OpLogicalNot for that purpose here.
73   iter.InsertBefore(MakeUnique<opt::Instruction>(
74       ir_context, SpvOpLogicalNot, condition_inst->type_id(),
75       message_.fresh_id(),
76       opt::Instruction::OperandList{
77           {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
78 
79   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
80 
81   // Update OpBranchConditional condition operand.
82   branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
83 
84   // Swap label operands.
85   std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
86 
87   // Additionally, swap branch weights if present.
88   if (branch_inst->NumInOperands() > 3) {
89     std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
90   }
91 
92   // Make sure the changes are analyzed.
93   ir_context->InvalidateAnalysesExceptFor(
94       opt::IRContext::Analysis::kAnalysisNone);
95 }
96 
97 protobufs::Transformation
ToMessage() const98 TransformationSwapConditionalBranchOperands::ToMessage() const {
99   protobufs::Transformation result;
100   *result.mutable_swap_conditional_branch_operands() = message_;
101   return result;
102 }
103 
104 std::unordered_set<uint32_t>
GetFreshIds() const105 TransformationSwapConditionalBranchOperands::GetFreshIds() const {
106   return {message_.fresh_id()};
107 }
108 
109 }  // namespace fuzz
110 }  // namespace spvtools
111