• 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_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(protobufs::TransformationSwapConditionalBranchOperands message)24     TransformationSwapConditionalBranchOperands(
25         protobufs::TransformationSwapConditionalBranchOperands message)
26     : message_(std::move(message)) {}
27 
28 TransformationSwapConditionalBranchOperands::
TransformationSwapConditionalBranchOperands(const protobufs::InstructionDescriptor & instruction_descriptor,uint32_t fresh_id)29     TransformationSwapConditionalBranchOperands(
30         const protobufs::InstructionDescriptor& instruction_descriptor,
31         uint32_t fresh_id) {
32   *message_.mutable_instruction_descriptor() = instruction_descriptor;
33   message_.set_fresh_id(fresh_id);
34 }
35 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const36 bool TransformationSwapConditionalBranchOperands::IsApplicable(
37     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
38   const auto* inst =
39       FindInstruction(message_.instruction_descriptor(), ir_context);
40   return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
41          inst->opcode() == SpvOpBranchConditional;
42 }
43 
Apply(opt::IRContext * ir_context,TransformationContext *) const44 void TransformationSwapConditionalBranchOperands::Apply(
45     opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
46   auto* branch_inst =
47       FindInstruction(message_.instruction_descriptor(), ir_context);
48   assert(branch_inst);
49 
50   auto* block = ir_context->get_instr_block(branch_inst);
51   assert(block);
52 
53   // Compute the last instruction in the |block| that allows us to insert
54   // OpLogicalNot above it.
55   auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
56   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
57     // There might be a merge instruction before OpBranchConditional.
58     --iter;
59   }
60 
61   assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
62          "We should now be able to insert SpvOpLogicalNot before |iter|");
63 
64   // Get the instruction whose result is used as a condition for
65   // OpBranchConditional.
66   const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
67       branch_inst->GetSingleWordInOperand(0));
68   assert(condition_inst);
69 
70   // We are swapping the labels in OpBranchConditional. This means that we must
71   // invert the guard as well. We are using OpLogicalNot for that purpose here.
72   auto new_instruction = MakeUnique<opt::Instruction>(
73       ir_context, SpvOpLogicalNot, condition_inst->type_id(),
74       message_.fresh_id(),
75       opt::Instruction::OperandList{
76           {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}});
77   auto new_instruction_ptr = new_instruction.get();
78   iter.InsertBefore(std::move(new_instruction));
79 
80   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
81 
82   // Update OpBranchConditional condition operand.
83   branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
84 
85   // Swap label operands.
86   std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
87 
88   // Additionally, swap branch weights if present.
89   if (branch_inst->NumInOperands() > 3) {
90     std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
91   }
92 
93   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
94   ir_context->set_instr_block(new_instruction_ptr, block);
95   ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(branch_inst);
96   ir_context->get_def_use_mgr()->AnalyzeInstUse(branch_inst);
97 
98   // No analyses need to be invalidated since the transformation is local to a
99   // block and the def-use and instruction-to-block mappings have been updated.
100 }
101 
102 protobufs::Transformation
ToMessage() const103 TransformationSwapConditionalBranchOperands::ToMessage() const {
104   protobufs::Transformation result;
105   *result.mutable_swap_conditional_branch_operands() = message_;
106   return result;
107 }
108 
109 std::unordered_set<uint32_t>
GetFreshIds() const110 TransformationSwapConditionalBranchOperands::GetFreshIds() const {
111   return {message_.fresh_id()};
112 }
113 
114 }  // namespace fuzz
115 }  // namespace spvtools
116